diff --git a/dictionaries/en.ts b/dictionaries/en.ts index e710f28..a236f37 100644 --- a/dictionaries/en.ts +++ b/dictionaries/en.ts @@ -51,12 +51,41 @@ export const enDictionary: Dictionary = { httpError: "HTTP Error", }, admin: { - validate: "Validate blogs", - newblog: "New blog", - edit: "Edit blogs", - form: "Form submissions", - users: "Users management", - account: "My account", + manageaccount: { + validate: "Validate blogs", + newblog: "New blog", + edit: "Edit blogs", + form: "Form submissions", + users: "Users management", + account: "My account", + }, + createblog: { + title: "Create a new post", + formentry: "Title", + new: "Create", + edit: "Edit an existing post", + }, + editblog: { + rename: { + title: "Title", + rename: "Rename", + modifytitle: "Modification of the post title", + }, + labels: { + modifylabels: "Modify the labels", + selectormessage: "Select 6 labels at most", + empty: "No filter correspond to this search", + }, + save: { + saving: "Saving in progress", + saved: "Saved", + automaticsave: "Automatic save of posts", + savedescription: + "An automatic backup is constantly being made to prevent data loss. As they can take time, you can make a manual backup before leaving the page with the 'Save' button. Spamming this button is useless, just can lead to crashes.", + savewarning: "WARNING: do not leave the page until the status is marked 'Saved' or you may lose your content...", + save: "Save", + }, + }, }, sitemap: { title: "Site plan", diff --git a/dictionaries/fr.ts b/dictionaries/fr.ts index 906ef17..92cef27 100644 --- a/dictionaries/fr.ts +++ b/dictionaries/fr.ts @@ -1,3 +1,5 @@ +import { empty } from "@prisma/client/runtime/library"; + export const frDictionary = { navigation: { cookies: { @@ -48,12 +50,41 @@ export const frDictionary = { httpError: "Erreur HTTP", }, admin: { - validate: "Valider des posts", - newblog: "Nouveau blog", - edit: "Modifier des posts", - form: "Soumissions au form", - users: "Gestion des utilisateurs", - account: "Mon compte", + manageaccount: { + validate: "Valider des posts", + newblog: "Nouveau blog", + edit: "Modifier des posts", + form: "Soumissions au form", + users: "Gestion des utilisateurs", + account: "Mon compte", + }, + createblog: { + title: "Création d'un post", + formentry: "Titre", + new: "Créer", + edit: "Editer un post existant", + }, + editblog: { + rename: { + title: "Titre", + rename: "Renommer", + modifytitle: "Modification du titre du post", + }, + labels: { + modifylabels: "Modifier les labels", + selectormessage: "Selectionner au plus 6 labels", + empty: "Aucun filtre ne correspond à cette recherche", + }, + save: { + saving: "Sauvegarde en cours", + saved: "Sauvegardé", + automaticsave: "Sauvegarde automatique des posts", + savedescription: + 'Une sauvegarde automatique s\'effectue en permanence pour éviter les pertes de données. Comme elles peuvent prendre du temps, vous pouvez faire une sauvegarde manuelle avant de quitter la page avec le bouton "Sauvegarder". Spammer ce bouton ne sert à rien, juste peut entraîner des crash.', + savewarning: 'ATTENTION: ne quittez pas la page tant que le status n\'est pas marqué en "Sauvegardé" au risque de perdre votre contenu...', + save: "Sauvegarder", + }, + }, }, sitemap: { title: "Plan du site", diff --git a/prisma/add-slugtr.ts b/prisma/add-slugtr.ts new file mode 100644 index 0000000..9257fcc --- /dev/null +++ b/prisma/add-slugtr.ts @@ -0,0 +1,83 @@ +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +async function updateSlugTr() { + try { + // Ajoute la colonne "slugtr" si elle n'existe pas déjà + await prisma.$executeRawUnsafe(` + ALTER TABLE Post ADD COLUMN slugtr TEXT; + `); + + console.log('La colonne "slugtr" a été ajoutée'); + } catch (error) { + console.error("Erreur à l'ajout de la colonne", error); + } + + var posts = await prisma.post.findMany({ + where: { + locale: "fr", + }, + }); + + for (const post of posts) { + await prisma.post.update({ + where: { id: post.id }, + data: { + slugtr: post.title + "_pas_traduit", + }, + }); + } + + posts = await prisma.post.findMany({ + where: { + locale: "en", + }, + }); + + for (const post of posts) { + await prisma.post.update({ + where: { id: post.id }, + data: { + slugtr: post.title + "_not_translated", + }, + }); + } + // Cette partie ne devrait pas être incluse si les posts ont une version en français et en anglais car ces derniers seraient dupliqués. + const posts_to_dupe = await prisma.post.findMany({ + include: { + authors: true, + labels: true, + }, + }); + + for (const post of posts_to_dupe) { + const newLocale = post.locale === "fr" ? "en" : "fr"; + + await prisma.post.create({ + data: { + title: post.title + newLocale === "fr" ? "_not_translated" : "_pas_traduit", + locale: newLocale, + slug: post.slugtr === null ? "undefined" : post.slugtr, + slugtr: post.slug, + authors: { + connect: post.authors.map(author => ({ id: author.id })), + }, + content: "", + validated: false, + labels: { + connect: post.labels.map(label => ({ id: label.id })), + }, + }, + }); + } +} +updateSlugTr() + .then(() => { + console.log("Mise à jour terminée"); + prisma.$disconnect(); + }) + .catch(error => { + console.error("Erreur :", error); + prisma.$disconnect(); + }); diff --git a/prisma/migrations/20250324113727_add_slugtr/migration.sql b/prisma/migrations/20250324113727_add_slugtr/migration.sql new file mode 100644 index 0000000..6eafdfc --- /dev/null +++ b/prisma/migrations/20250324113727_add_slugtr/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Post" ADD COLUMN "slugtr" TEXT; diff --git a/prisma/migrations/20250324113809_add_slugtr_unique/migration.sql b/prisma/migrations/20250324113809_add_slugtr_unique/migration.sql new file mode 100644 index 0000000..30e4c27 --- /dev/null +++ b/prisma/migrations/20250324113809_add_slugtr_unique/migration.sql @@ -0,0 +1,28 @@ +/* + Warnings: + + - Made the column `slugtr` on table `Post` required. This step will fail if there are existing NULL values in that column. + +*/ +-- RedefineTables +PRAGMA defer_foreign_keys=ON; +PRAGMA foreign_keys=OFF; +CREATE TABLE "new_Post" ( + "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + "title" TEXT NOT NULL, + "locale" TEXT NOT NULL, + "slug" TEXT NOT NULL, + "slugtr" TEXT NOT NULL, + "content" TEXT NOT NULL, + "validated" BOOLEAN NOT NULL DEFAULT false, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); +INSERT INTO "new_Post" ("content", "createdAt", "id", "locale", "slug", "slugtr", "title", "updatedAt", "validated") SELECT "content", "createdAt", "id", "locale", "slug", "slugtr", "title", "updatedAt", "validated" FROM "Post"; +DROP TABLE "Post"; +ALTER TABLE "new_Post" RENAME TO "Post"; +CREATE UNIQUE INDEX "Post_id_key" ON "Post"("id"); +CREATE UNIQUE INDEX "Post_slug_key" ON "Post"("slug"); +CREATE UNIQUE INDEX "Post_slugtr_key" ON "Post"("slugtr"); +PRAGMA foreign_keys=ON; +PRAGMA defer_foreign_keys=OFF; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0eba9f6..0e3d914 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -39,6 +39,7 @@ model Post { title String locale String slug String @unique + slugtr String @unique authors User[] content String validated Boolean @default(false) diff --git a/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/editor-actions.tsx b/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/editor-actions.tsx index b4ed097..9b0702e 100644 --- a/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/editor-actions.tsx +++ b/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/editor-actions.tsx @@ -15,37 +15,50 @@ import { useState } from "react"; import { getBlog, renameBlog } from "@/db/blogs"; import { updatePostLabels } from "@/db/labels"; import { Locale } from "@/locales/config"; +import { Dictionary, getDictionary } from "@/locales/dictionaries"; -function Rename({ title, id, router }: { title: string; id: number; router: AppRouterInstance }) { +function Rename({ + title, + id, + router, + t, + locale, +}: { + title: string; + id: number; + router: AppRouterInstance; + t: Dictionary["navigation"]["admin"]["editblog"]; + locale: Locale; +}) { return ( - Modification du titre du post + {t.rename.modifytitle}
{ const formData = new FormData(e.target as HTMLFormElement); const title = formData.get("title") as string; - renameBlog(id, title).finally(() => { + renameBlog(id, title, locale).finally(() => { router.refresh(); }); }} >
@@ -54,23 +67,33 @@ function Rename({ title, id, router }: { title: string; id: number; router: AppR ); } -function AddLabel({ getLabels, addRemoveLabel, dbLabels }: { getLabels: string[]; addRemoveLabel: (x: string) => void; dbLabels: string[] }) { +function AddLabel({ + getLabels, + addRemoveLabel, + dbLabels, + t, +}: { + getLabels: string[]; + addRemoveLabel: (x: string) => void; + dbLabels: string[]; + t: Dictionary["navigation"]["admin"]["editblog"]; +}) { return ( ); } -function OpenSave({ saving }: { saving: boolean }) { +function OpenSave({ saving, t }: { saving: boolean; t: Dictionary["navigation"]["admin"]["editblog"] }) { return ( @@ -78,12 +101,12 @@ function OpenSave({ saving }: { saving: boolean }) { {saving ? ( <> -

Sauvegarde en cours

+

{t.save.saving}

) : ( <> -

Sauvegardé

+

{t.save.saved}

)} @@ -92,16 +115,9 @@ function OpenSave({ saving }: { saving: boolean }) {
- Sauvegarde automatique des posts - - Une sauvegarde automatique s'effectue en permanence pour éviter les pertes de données. Comme elles peuvent prendre du temps, vous - pouvez faire une sauvegarde manuelle avant de quitter la page avec le bouton “Sauvegarder”. Spammer ce bouton ne sert à - rien, juste peut entraîner des crash. - - - ATTENTION: ne quittez pas la page tant que le status n'est pas marqué en “Sauvegardé” au risque de perdre votre - contenu... - + {t.save.automaticsave} + {t.save.savedescription} + {t.save.savewarning}
@@ -120,6 +136,7 @@ interface ActionProps { } export function Actions({ setToBeChanged, content, value, title, id, dbLabels, blogLabels, locale }: ActionProps) { + const t = getDictionary(locale).navigation.admin.editblog; const [getLabels, setLabels] = useState(blogLabels); const addRemoveLabel = (label: string) => { var newLabels = getLabels; @@ -150,15 +167,15 @@ export function Actions({ setToBeChanged, content, value, title, id, dbLabels, b }} > -

Sauvergarder

+

{t.save.save}

- - + +
- +
{/*
diff --git a/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/page.tsx b/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/page.tsx index 5ba2740..2bd84c3 100644 --- a/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/page.tsx +++ b/src/app/[locale]/(blog)/(admin)/edit-blog/[postId]/page.tsx @@ -29,7 +29,7 @@ export default async function EditBlog({ params: { postId, locale } }: LocalePos

{blog.title}

{blog.validated && } router.refresh()); + updateLocaleBlogContent(id, newContent, locale).finally(() => router.refresh()); } }); setLoaded(true); } - }, [quill, content, id, router, loaded]); + }, [quill, content, id, router, loaded, locale]); return (
diff --git a/src/app/[locale]/(blog)/(admin)/list-blog/layout.tsx b/src/app/[locale]/(blog)/(admin)/list-blog/layout.tsx index 6755091..6a979ce 100644 --- a/src/app/[locale]/(blog)/(admin)/list-blog/layout.tsx +++ b/src/app/[locale]/(blog)/(admin)/list-blog/layout.tsx @@ -14,10 +14,11 @@ interface PageProps extends LocaleParams { export default async function Validation({ params: { locale }, validate }: PageProps) { const posts = await getAllBlog(); - const allData: ValidationBlogType[] = posts.map(({ id, validated, authors, title, content, createdAt, updatedAt }) => ({ + const allData: ValidationBlogType[] = posts.map(({ id, validated, authors, locale, title, content, createdAt, updatedAt }) => ({ id, validated, emails: authors.map(author => author.email), + locale, title, content, createdAt, diff --git a/src/app/[locale]/(blog)/(admin)/list-blog/select/schema.ts b/src/app/[locale]/(blog)/(admin)/list-blog/select/schema.ts index 3014135..31526da 100644 --- a/src/app/[locale]/(blog)/(admin)/list-blog/select/schema.ts +++ b/src/app/[locale]/(blog)/(admin)/list-blog/select/schema.ts @@ -4,6 +4,7 @@ export const validationBlogSchema = z.object({ id: z.number(), validated: z.boolean(), emails: z.array(z.string()), + locale: z.string(), title: z.string(), content: z.string(), createdAt: z.date(), diff --git a/src/app/[locale]/(blog)/(admin)/new-blog/client.tsx b/src/app/[locale]/(blog)/(admin)/new-blog/client.tsx index e0f8e88..5cd0ee6 100644 --- a/src/app/[locale]/(blog)/(admin)/new-blog/client.tsx +++ b/src/app/[locale]/(blog)/(admin)/new-blog/client.tsx @@ -10,6 +10,7 @@ import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, For import { Input } from "@/components/ui/input"; import { nav } from "@/locales/routing"; import { DEFAULT_LOCALE, LOCALES, Locale } from "@/locales/config"; +import { getDictionary } from "@/locales/dictionaries"; export const newPostSchema = z.object({ title: z.string().min(2, { @@ -18,7 +19,7 @@ export const newPostSchema = z.object({ locale: z.enum(LOCALES), }); -export default function NewPostForm({ email }: { email: string }) { +export default function NewPostForm({ email, locale }: { email: string; locale: Locale }) { const form = useForm>({ resolver: zodResolver(newPostSchema), defaultValues: { @@ -26,10 +27,12 @@ export default function NewPostForm({ email }: { email: string }) { }, }); + const t = getDictionary(locale).navigation.admin.createblog; + const router = useRouter(); const onSubmit = async (values: z.infer) => { - const apiData = JSON.stringify({ authorEmail: email, title: values.title, locale: "fr" }); + const apiData = JSON.stringify({ authorEmail: email, title: values.title, locale: values.locale }); const response = await fetch("/api/create-blog", { method: "POST", headers: { @@ -39,11 +42,11 @@ export default function NewPostForm({ email }: { email: string }) { }); if (!response.ok) { console.error("Error creating blog: ", response.status, response.body); - router.push(nav("fr", "/errors/500")); + router.push(nav(locale, "/errors/500")); } else { const id = (await response.json()).blogId; if (process.env.DEV_MODE) console.log("Created POST with ID = ", id); - router.push(nav("fr", `/edit-blog/${id}`)); + router.push(nav(locale, `/edit-blog/${id}`)); } }; @@ -57,13 +60,13 @@ export default function NewPostForm({ email }: { email: string }) { render={({ field }) => ( - + )} /> - {/* ( @@ -73,10 +76,8 @@ export default function NewPostForm({ email }: { email: string }) { {...field} defaultValue={DEFAULT_LOCALE} className="p-2 rounded-md rounded-l-none" - value={selectedLocale} onChange={e => { form.setValue("locale", e.target.value as Locale); - setSelectedLocale(e.target.value as Locale); }} > {LOCALES.map(l => ( @@ -89,10 +90,10 @@ export default function NewPostForm({ email }: { email: string }) { )} - /> */} + />
diff --git a/src/app/[locale]/(blog)/(admin)/new-blog/page.tsx b/src/app/[locale]/(blog)/(admin)/new-blog/page.tsx index e375278..751f70c 100644 --- a/src/app/[locale]/(blog)/(admin)/new-blog/page.tsx +++ b/src/app/[locale]/(blog)/(admin)/new-blog/page.tsx @@ -5,22 +5,24 @@ import { Button } from "@/components/ui/button"; import Link from "next/link"; import { LocaleParams } from "@/locales/config"; import { nav } from "@/locales/routing"; +import { getDictionary } from "@/locales/dictionaries"; export default async function NewBlog({ params: { locale } }: LocaleParams) { const session = await auth(); const email = session?.user?.email as string; + const t = getDictionary(locale).navigation.admin.createblog; return (
- Création d'un post + {t.title} - + diff --git a/src/app/[locale]/(blog)/(client)/blog/[postId]/page.tsx b/src/app/[locale]/(blog)/(client)/blog/[postId]/page.tsx index 3940d03..05896e2 100644 --- a/src/app/[locale]/(blog)/(client)/blog/[postId]/page.tsx +++ b/src/app/[locale]/(blog)/(client)/blog/[postId]/page.tsx @@ -11,7 +11,7 @@ export default async function BlogPage({ params: { locale, postId } }: LocalePos if (!posts) { redirect(nav(locale, "/error/404")); } - const localePost = posts.find(blog => blog.slug === slug); + const localePost = posts.find(blog => blog.slug === slug || blog.slugtr === slug); if (!localePost) { redirect(nav(locale, "/error/404")); } else { diff --git a/src/app/[locale]/(blog)/(client)/blog/client.tsx b/src/app/[locale]/(blog)/(client)/blog/client.tsx index 831e8e0..19bff18 100644 --- a/src/app/[locale]/(blog)/(client)/blog/client.tsx +++ b/src/app/[locale]/(blog)/(client)/blog/client.tsx @@ -24,6 +24,7 @@ export interface PostPresentation { date: Date; labels: string[]; slug: string; + slugtr: string | null; } const allLabelsInValue = (postLabels: string[], selectedLabels: string[]) => @@ -78,7 +79,6 @@ function BlogsList({ posts, locale, t_none }: { t_none: string; posts: PostPrese ) : null} */}

{post.displayedAuthors}

-

{post.authors}

{Object.values(post.labels).map((label, i) => (

diff --git a/src/app/api/create-blog/route.ts b/src/app/api/create-blog/route.ts index 0501654..05e000e 100644 --- a/src/app/api/create-blog/route.ts +++ b/src/app/api/create-blog/route.ts @@ -7,7 +7,9 @@ export async function POST(request: Request) { const { authorEmail, title, locale } = await request.json(); const session = await auth(); if (session?.user.email !== authorEmail) throw new Error("Email mismatch: Not allowed"); - const blogId = await createBlog(authorEmail, title, locale); + const titletr = locale === "fr" ? title + "_pas_traduit" : title + "_not_translated"; + const blogId = await createBlog(authorEmail, title, locale, titletr); + const blogIdTr = await createBlog(authorEmail, titletr, locale === "fr" ? "en" : "fr", title); return NextResponse.json({ blogId }); } catch (error) { console.error("[API Error] Failed to create blog:", error); diff --git a/src/components/navigation/navbar/links/get-links.ts b/src/components/navigation/navbar/links/get-links.ts index 16063e4..99870e5 100644 --- a/src/components/navigation/navbar/links/get-links.ts +++ b/src/components/navigation/navbar/links/get-links.ts @@ -20,17 +20,17 @@ export function getLinks(locale: Locale, user: ExtendedUser) { var authLinks: SingleLink[] = []; if (user?.rights?.blogAdmin) { - authLinks.push({ href: nav(locale, "/list-blog"), title: t.admin.validate }); + authLinks.push({ href: nav(locale, "/list-blog"), title: t.admin.manageaccount.validate }); } if (user?.rights?.blogAuthor) { - authLinks.push({ href: nav(locale, "/new-blog"), title: t.admin.newblog }); - authLinks.push({ href: nav(locale, "/list-blog"), title: t.admin.edit }); + authLinks.push({ href: nav(locale, "/new-blog"), title: t.admin.manageaccount.newblog }); + authLinks.push({ href: nav(locale, "/list-blog"), title: t.admin.manageaccount.edit }); } if (user?.rights?.formAdmin) { - authLinks.push({ href: nav(locale, "/form-submission"), title: t.admin.form }); + authLinks.push({ href: nav(locale, "/form-submission"), title: t.admin.manageaccount.form }); } if (user?.rights?.userAdmin) { - authLinks.push({ href: nav(locale, "/users"), title: t.admin.users }); + authLinks.push({ href: nav(locale, "/users"), title: t.admin.manageaccount.users }); } const logoutLink = { href: nav(locale, "/auth/signout"), title: s.logout }; authLinks.push(logoutLink); @@ -41,7 +41,7 @@ export function getLinks(locale: Locale, user: ExtendedUser) { : authLinks.length <= 1 ? logoutLink : { - title: t.admin.account, + title: t.admin.manageaccount.account, links: authLinks, }; diff --git a/src/components/navigation/navbar/links/locale-switcher.tsx b/src/components/navigation/navbar/links/locale-switcher.tsx index f9bb6c8..8cc1750 100644 --- a/src/components/navigation/navbar/links/locale-switcher.tsx +++ b/src/components/navigation/navbar/links/locale-switcher.tsx @@ -18,7 +18,7 @@ const useLocaledUrl = (locale: Locale) => { if (index === split.length) { return nav(locale, "/"); } else if (isLocale(split[index])) { - return nav(locale, "/" + split.slice(index + 1).join("/")); + return nav(locale, "/" + split.splice(index + 1).join("/")); } else { return nav(locale, split.join("/")); } @@ -35,7 +35,7 @@ export const LocaleSwitch = ({ onClick: () => void; setOpened: (open: null | number) => void; }) => { - const localedUrl = useLocaledUrl(locale == "fr" ? "en" : "fr"); + const localedUrl = useLocaledUrl(locale === "fr" ? "en" : "fr"); return ( diff --git a/src/db/blogs.ts b/src/db/blogs.ts index ec34f5d..0ffd8d7 100644 --- a/src/db/blogs.ts +++ b/src/db/blogs.ts @@ -7,7 +7,7 @@ import { Label, Post, User } from "@prisma/client"; import { Op } from "quill/core"; import { generateSlug } from "./slug"; -export async function createBlog(authorEmail: string, title: string, locale: Locale): Promise { +export async function createBlog(authorEmail: string, title: string, locale: Locale, titletr: string): Promise { try { if (!authorEmail) { throw new Error("Author email is undefined"); @@ -22,8 +22,9 @@ export async function createBlog(authorEmail: string, title: string, locale: Loc connect: [{ id: author.id }], }, locale, - title, + title: title, slug: generateSlug(title), + slugtr: generateSlug(titletr), content: "[]", }, }); @@ -34,7 +35,7 @@ export async function createBlog(authorEmail: string, title: string, locale: Loc } } -export async function updateLocaleBlogContent(id: number, content: Op[]) { +export async function updateLocaleBlogContent(id: number, content: Op[], locale: Locale) { try { await db.post.update({ where: { id: id }, @@ -45,7 +46,7 @@ export async function updateLocaleBlogContent(id: number, content: Op[]) { } } -export async function getBlogContent(id: number): Promise { +export async function getBlogContent(id: number, locale: Locale): Promise { try { const blog = await db.post.findUnique({ where: { id: id }, @@ -64,7 +65,7 @@ export async function getValidatedBlogs(locale: Locale): Promise blog.validated && blog.locale == locale) - .map(({ authors, labels, updatedAt, ...blog }) => ({ + .map(({ authors, labels, updatedAt, locale, ...blog }) => ({ authors: authors.map(author => author.name), emails: authors.map(author => author.email), date: updatedAt, @@ -106,7 +107,7 @@ export async function deleteBlog(id: number) { } } -export async function renameBlog(id: number, title: string) { +export async function renameBlog(id: number, title: string, locale: Locale) { try { await db.post.update({ where: { id: id }, diff --git a/src/lib/users.ts b/src/lib/users.ts index 176b5f6..33b1a35 100644 --- a/src/lib/users.ts +++ b/src/lib/users.ts @@ -16,14 +16,17 @@ export function getUserName(email: string) { } export function getAuthors(authors: string[]) { - const last = authors.pop(); - const beforeLast = authors.pop(); - if (!last) { + const last = authors[authors.length - 1]; + const beforeLast = authors[authors.length - 2]; + if (authors.length === 0 || !authors[authors.length - 1]) { console.error("Error while fetching user data."); return ""; - } else if (!beforeLast) { + } else if (authors.length === 1 || !authors[authors.length - 2]) { + const last = authors[authors.length - 1]; return last; } else { + const last = authors[authors.length - 1]; + const beforeLast = authors[authors.length - 2]; return authors.reduce((acc, author) => `${acc}${author}, `, "") + `${beforeLast} & ${last}`; } }