Heim > Web-Frontend > js-Tutorial > Erstellen eines dynamischen Blog-Dashboards mit Next.js

Erstellen eines dynamischen Blog-Dashboards mit Next.js

Barbara Streisand
Freigeben: 2024-12-08 17:04:10
Original
637 Leute haben es durchsucht

Einführung

Hallo, wie geht es dir? Hier ist Vítor, der mit einem neuen Projekt zurückkommt, das Ihnen dabei helfen soll, Ihre Programmierkenntnisse zu verbessern. Es ist schon eine Weile her, seit ich das letzte Mal ein Tutorial veröffentlicht habe. In den letzten Monaten habe ich mir etwas Zeit genommen, um mich auszuruhen und mich auf andere Aktivitäten zu konzentrieren. In dieser Zeit habe ich ein kleines Webprojekt entwickelt: einen Blog, der zum Schwerpunkt dieses Tutorials wurde.

In dieser Anleitung erstellen wir das Frontend einer Blog-Seite, die Markdown rendern kann. Die Anwendung umfasst öffentliche und private Routen, Benutzerauthentifizierung und die Möglichkeit, Markdown-Text zu schreiben, Fotos hinzuzufügen, Artikel anzuzeigen und vieles mehr.

Sie können Ihre Anwendung jederzeit nach Ihren Wünschen anpassen – ich ermutige Sie sogar dazu.

Sie können hier auf das Repository für diese Anwendung zugreifen:

Building a Dynamic Blog Dashboard with Next.js Gondrak08 / Blog-Plattform

Eine Blog-Plattform, die mit Next.js/typescript erstellt wurde.

Plataforma para blog

  • Tutorial im Text

Zutaten

  • next-auth – Authentifizierungsbibliothek für Next.js
  • github.com/markdown-it/markdown-it – Markdown-Bibliothek.
  • github.com/sindresorhus/github-markdown-css- Für unseren Editor-Markdown-Stil.
  • github.com/remarkjs/react-markdown – Bibliothek zum Rendern von Markdown auf unseren reagierenden Komponenten.
  • github.com/remarkjs/remark-react/tree/4722bdf – Plugin zur Transformation von Markdown in React.
  • codemirror.net – Editor für Webkomponenten.
  • React-Icons – Lib de Icones para React.

Como usar

npm i
npm run start
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Server

Sie können den Server Ihrer Anwendung auf dem Server finden


Auf GitHub ansehen


Dieses Tutorial beinhaltet auch das Schreiben des Node.js-Servers, der in diesem Handbuch verwendet wird:

Ich hoffe es gefällt euch.

Viel Spaß beim Codieren!

Bibliotheken

Hier ist eine Zusammenfassung der in diesem Projekt verwendeten Bibliotheken:

  • next-auth – Authentifizierungsbibliothek für Next.js
  • github.com/markdown-it/markdown-it – Markdown-Bibliothek.
  • github.com/sindresorhus/github-markdown-css – Für die Gestaltung unseres Markdown-Editors.
  • github.com/remarkjs/react-markdown – Bibliothek zum Rendern von Markdown in unserer React-Komponente.
  • github.com/remarkjs/remark-react/tree/4722bdf – Plugin zur Umwandlung von Markdown in React.
  • codemirror.net – Webkomponenten-Editor.
  • React-Icons – Symbolbibliothek für React.

Erstellen des React-Projekts

Wir werden die neueste Version des Next.js-Frameworks verwenden, die zum Zeitpunkt der Erstellung dieses Tutorials Version 13.4 ist.

Führen Sie den folgenden Befehl aus, um das Projekt zu erstellen:

npm i
npm run start
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Wählen Sie während der Installation die Vorlageneinstellungen aus. In diesem Tutorial verwende ich TypeScript als Programmiersprache und das Tailwind CSS-Framework zum Gestalten unserer Anwendung.

Konfiguration

Jetzt installieren wir alle Bibliotheken, die wir verwenden werden.

Abschlag
npx create-next-app myblog
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Bemerkung reagieren
npm i  markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Codespiegel
remark remark-gfm remark-react
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Symbole
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Bereinigen Sie dann die ursprüngliche Struktur Ihrer Installation, indem Sie alles entfernen, was wir nicht verwenden werden.

Architektur

Dies ist die endgültige Struktur unserer Bewerbung.

npm i react-icons @types/react-icons
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Erste Schritte

next.config konfigurieren

Im Stammverzeichnis des Projekts, in der Datei next.config.js, konfigurieren wir die Domänenadresse, von der aus wir auf die Bilder für unsere Artikel zugreifen. Für dieses Tutorial oder wenn Sie einen lokalen Server verwenden, verwenden wir localhost.

Stellen Sie sicher, dass Sie diese Konfiguration einschließen, um das korrekte Laden von Bildern in Ihrer Anwendung sicherzustellen.

src-
  |- app/
  |    |-(pages)/
  |    |      |- (private)/
  |    |      |       |- (home)
  |    |      |       |- editArticle/[id]
  |    |      |       |
  |    |      |       |- newArticle
  |    |      | - (public)/
  |    |              | - article/[id]
  |    |              | - login
  |    |
  |   api/
  |    |- auth/[...nextAuth]/route.ts
  |    |- global.css
  |    |- layout.tsx
  |
  | - components/
  | - context/
  | - interfaces/
  | - lib/
  | - services/
middleware.ts
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Middleware konfigurieren

Erstellen Sie im Stammordner der Anwendung src/ eine middleware.ts, um den Zugriff auf private Routen zu überprüfen.

const nextConfig = {
   images: {
    domains: ["localhost"],
  },
};
Nach dem Login kopieren
Nach dem Login kopieren

Um mehr über Middlewares und alles, was Sie damit machen können, zu erfahren, schauen Sie sich die Dokumentation an.

Konfigurieren der Authentifizierungsroute

Erstellen Sie im Ordner /app eine Datei mit dem Namen route.ts in api/auth/[...nextauth]. Es enthält die Konfiguration für unsere Routen und stellt über den CredentialsProvider eine Verbindung zu unserer Authentifizierungs-API her.

Der CredentialsProvider ermöglicht Ihnen die Anmeldung mit beliebigen Anmeldeinformationen wie Benutzername und Passwort, Domäne, Zwei-Faktor-Authentifizierung, Hardwaregerät usw.

Erstellen Sie zunächst im Stammverzeichnis Ihres Projekts eine .env.local-Datei und fügen Sie ein Token hinzu, das als unser Geheimnis verwendet wird.

npm i
npm run start
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Als nächstes schreiben wir unser Authentifizierungssystem, in dem dieses NEXTAUTH_SECRET zu unserem Geheimnis in der Datei src/app/auth/[...nextauth]/routes.ts hinzugefügt wird.

npx create-next-app myblog
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Authentifizierungsanbieter

Lassen Sie uns einen Authentifizierungsanbieter erstellen, einen Kontext, der die Daten unserer Benutzer auf den Seiten unserer privaten Route teilt. Wir werden es später verwenden, um eine unserer layout.tsx-Dateien zu verpacken.

Erstellen Sie eine Datei in src/context/auth-provider.tsx mit folgendem Inhalt:

npm i  markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Globale Stile

Insgesamt werden wir in unserer Anwendung Tailwind CSS verwenden, um unser Styling zu erstellen. An einigen Stellen werden wir jedoch benutzerdefinierte CSS-Klassen zwischen Seiten und Komponenten teilen.

remark remark-gfm remark-react
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Layouts

Jetzt schreiben wir die Layouts, sowohl privat als auch öffentlich.

app/layout.tsx

npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

seiten/layout.tsx

npm i react-icons @types/react-icons
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

API-Aufrufe

Unsere Anwendung führt mehrere Aufrufe an unsere API durch, und Sie können diese Anwendung so anpassen, dass sie jede externe API verwendet. In unserem Beispiel verwenden wir unsere lokale Anwendung. Wenn Sie das Backend-Tutorial und die Servererstellung noch nicht gesehen haben, schauen Sie sich das an.

In src/services/ schreiben wir die folgenden Funktionen:

  1. authService.ts: Funktion, die für die Authentifizierung unseres Benutzers auf dem Server verantwortlich ist.
src-
  |- app/
  |    |-(pages)/
  |    |      |- (private)/
  |    |      |       |- (home)
  |    |      |       |- editArticle/[id]
  |    |      |       |
  |    |      |       |- newArticle
  |    |      | - (public)/
  |    |              | - article/[id]
  |    |              | - login
  |    |
  |   api/
  |    |- auth/[...nextAuth]/route.ts
  |    |- global.css
  |    |- layout.tsx
  |
  | - components/
  | - context/
  | - interfaces/
  | - lib/
  | - services/
middleware.ts
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

2.refreshAccessToken.tsx:

const nextConfig = {
   images: {
    domains: ["localhost"],
  },
};
Nach dem Login kopieren
Nach dem Login kopieren
  1. getArticles.tsx: Funktion, die für das Abrufen aller in unserer Datenbank gespeicherten Artikel verantwortlich ist:
export { default } from "next-auth/middleware";
export const config = {
  matcher: ["/", "/newArticle/", "/article/", "/article/:path*"],
};
Nach dem Login kopieren
  1. postArticle.tsx: Funktion, die für die Übermittlung der Artikeldaten an unseren Server verantwortlich ist.
.env.local
NEXTAUTH_SECRET = SubsTituaPorToken
Nach dem Login kopieren
  1. editArticle.tsx: Funktion, die für die Änderung eines bestimmten Artikels in der Datenbank verantwortlich ist.
import NextAuth from "next-auth/next";
import type { AuthOptions } from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { authenticate } from "@/services/authService";
import refreshAccessToken from "@/services/refreshAccessToken";

export const authOptions: AuthOptions = {
  providers: [
    CredentialsProvider({
      name: "credentials",
      credentials: {
        email: {
          name: "email",
          label: "email",
          type: "email",
          placeholder: "Email",
        },
        password: {
          name: "password",
          label: "password",
          type: "password",
          placeholder: "Password",
        },
      },
      async authorize(credentials, req) {
        if (typeof credentials !== "undefined") {
          const res = await authenticate({
            email: credentials.email,
            password: credentials.password,
          });
          if (typeof res !== "undefined") {
            return { ...res };
          } else {
            return null;
          }
        } else {
          return null;
        }
      },
    }),
  ],

  session: { strategy: "jwt" },
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async jwt({ token, user, account }: any) {
      if (user && account) {
        return {
          token: user?.token,
          accessTokenExpires: Date.now() + parseInt(user?.expiresIn, 10),
          refreshToken: user?.tokenRefresh,
        };
      }

      if (Date.now() < token.accessTokenExpires) {
        return token;
      } else {
        const refreshedToken = await refreshAccessToken(token.refreshToken);
        return {
          ...token,
          token: refreshedToken.token,
          refreshToken: refreshedToken.tokenRefresh,
          accessTokenExpires:
            Date.now() + parseInt(refreshedToken.expiresIn, 10),
        };
      }
    },
    async session({ session, token }) {
      session.user = token;
      return session;
    },
  },

  pages: {
    signIn: "/login",
    signOut: "/login",
  },
};

const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
Nach dem Login kopieren
  1. deleteArticle.tsx: Funktion, die für das Entfernen eines bestimmten Artikels aus unserer Datenbank verantwortlich ist.
'use client';
import React from 'react';
import { SessionProvider } from "next-auth/react";
export default function Provider({
    children,
    session
}: {
    children: React.ReactNode,
    session: any
}): React.ReactNode {
    return (
        <SessionProvider session={session} >
            {children}
        </SessionProvider>
    )
};
Nach dem Login kopieren

Komponenten

Als nächstes schreiben wir jede Komponente, die in der Anwendung verwendet wird.

Komponenten/Navbar.tsx

Eine einfache Komponente mit zwei Navigationslinks.

/*global.css*/
.container {
  max-width: 1100px;
  width: 100%;
  margin: 0px auto;
}

.image-container {
  position: relative;
  width: 100%;
  height: 5em;
  padding-top: 56.25%; /* Aspect ratio 16:9 (dividindo a altura pela largura) */
}

.image-container img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@keyframes spinner {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

.loading-spinner {
  width: 50px;
  height: 50px;
  border: 10px solid #f3f3f3;
  border-top: 10px solid #293d71;
  border-radius: 50%;
  animation: spinner 1.5s linear infinite;
}
Nach dem Login kopieren

Komponenten/Loading.tsx

Eine einfache Ladekomponente, die verwendet wird, während auf den Abschluss von API-Aufrufen gewartet wird.

import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import Provider from "@/context/auth-provider";
import { getServerSession } from "next-auth";
import { authOptions } from "./api/auth/[...nextauth]/route";
const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Markdown Text Editor",
  description: "Created by <@vitorAlecrim>",
};

export default async function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const session = await getServerSession(authOptions);
  return (
    <Provider session={session}>
      <html lang="en">
        <body className={inter.className}>{children}</body>
      </html>
    </Provider>
  );
}
Nach dem Login kopieren

Components/Pagination.tsx

Eine Paginierungskomponente, die auf unserer Seite verwendet wird und alle unsere Artikel auf unserem privaten Weg anzeigt. Einen ausführlicheren Artikel zum Schreiben dieser Komponente finden Sie hier

npm i
npm run start
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Components/ArticleCard.tsx

Eine Kartenkomponente zum Anzeigen geschriebener Artikel.

Diese Komponente enthält auch einen Link, der sowohl zur Artikelanzeigeseite als auch zur Seite zum Bearbeiten eines zuvor verfassten Artikels führt.

npx create-next-app myblog
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Components/ArticleList.tsx

Eine Komponente, die dafür verantwortlich ist, API-Aufrufe durchzuführen und die Antwort anzuzeigen.

Hier verwenden wir zwei API-Aufrufe über die von uns geschriebenen Funktionen:

  1. getArticles.ts – gibt alle Artikel zurück, die in der Komponente angezeigt werden.
  2. RemoveArticle – entfernt einen bestimmten Artikel aus unserer Liste und von unserem Server.

Wir werden die zuvor geschriebene Pagination.tsx-Komponente verwenden, um die Anzahl der Artikel auf Seiten aufzuteilen.

npm i  markdown-it @types/markdown-it markdown-it-style github-markdown-css react-markdown
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Seiten

Als nächstes gehen wir jede unserer Seiten durch, unterteilt nach ihren jeweiligen Routen.

Öffentliche Seiten

Login

Dies ist die Homepage unserer Bewerbung. Es handelt sich um eine einfache Seite, die Sie nach Belieben ändern können. Auf dieser Seite verwenden wir die Anmeldefunktion der Next-Auth-Navigationsbibliothek.

In der Datei src/app/pages/public/login/page.tsx.

remark remark-gfm remark-react
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Artikelseite

Um die Seite zum Lesen von Artikeln zu erstellen, entwickeln wir eine dynamische Seite.

Jede Blog-Plattform, die Sie besucht haben, verfügt wahrscheinlich über eine eigene Seite zum Lesen von Artikeln, auf die über eine URL zugegriffen werden kann. Der Grund hierfür ist eine dynamische Seitenweiterleitung. Glücklicherweise macht Next.js dies mit seiner neuen AppRouter-Methode einfach und macht unser Leben viel einfacher.

Zuerst müssen wir die Route in unserer Struktur erstellen, indem wir einen [id]-Ordner hinzufügen. Dies führt zu folgender Struktur: seiten/(public)/articles/[id]/pages.tsx.

  • Die ID entspricht dem Slug unserer Navigationsroute.
  • params ist eine Eigenschaft, die durch den Baum unserer Anwendung übergeben wird und den Navigations-Slug enthält.
npm @codemirror/commands @codemirror/highlight @codemirror/lang-javascript @codemirror/lang-markdown @codemirror/language @codemirror/language-data @codemirror/state @codemirror/theme-one-dark @codemirror/view
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Zweitens: Verwenden Sie die MarkdownIt-Bibliothek, um der Seite die Anzeige von Text im Markdown-Format zu ermöglichen.

npm i react-icons @types/react-icons
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Und schließlich

Sobald die Seite fertig ist, können Sie den Artikel mit der angegebenen ID anzeigen, indem Sie beispielsweise im Browser auf localhost:3000/articles/1 zugreifen.

In unserem Fall wird die ID durch die Navigation weitergegeben, wenn auf eine der ArticleCards.tsx-Komponenten geklickt wird, die auf der Hauptseite unserer privaten Route gerendert wird.

src-
  |- app/
  |    |-(pages)/
  |    |      |- (private)/
  |    |      |       |- (home)
  |    |      |       |- editArticle/[id]
  |    |      |       |
  |    |      |       |- newArticle
  |    |      | - (public)/
  |    |              | - article/[id]
  |    |              | - login
  |    |
  |   api/
  |    |- auth/[...nextAuth]/route.ts
  |    |- global.css
  |    |- layout.tsx
  |
  | - components/
  | - context/
  | - interfaces/
  | - lib/
  | - services/
middleware.ts
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Private Seiten

Hier sind unsere privaten Seiten, auf die nur zugegriffen werden kann, wenn der Benutzer in unserer Anwendung authentifiziert ist.

Heim

Wenn in unserem Ordner app/pages/ eine Datei in () deklariert wird, bedeutet dies, dass die Route / entspricht.

In unserem Fall bezieht sich der (Home-)Ordner auf die Homepage unserer privaten Route. Es ist die erste Seite, die der Benutzer sieht, wenn er sich beim System authentifiziert. Auf dieser Seite wird die Liste der Artikel aus unserer Datenbank angezeigt.

Die Daten werden von unserer ArticlesList.tsx-Komponente verarbeitet. Wenn Sie diesen Code noch nicht geschrieben haben, schauen Sie noch einmal im Abschnitt „Komponenten“ nach.

In app/(pages)/(private)/(home)/page.tsx.

npm i
npm run start
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Neuer Artikel

Dies ist eine der wichtigsten Seiten unserer Bewerbung, da sie uns die Registrierung unserer Artikel ermöglicht.

Auf dieser Seite kann der Benutzer:

  1. Schreiben Sie einen Artikel im Markdown-Format.
  2. Weisen Sie dem Artikel ein Bild zu.
  3. Sehen Sie sich eine Vorschau des Markdown-Textes an, bevor Sie ihn an den Server senden.

Die Seite verwendet mehrere Hooks:

  1. useCallback – wird zum Merken von Funktionen verwendet.
  2. useState – ermöglicht Ihnen das Hinzufügen einer Statusvariablen zu unserer Komponente.
  3. useSession – lässt uns überprüfen, ob der Benutzer authentifiziert ist und das Authentifizierungstoken erhalten.

Dazu verwenden wir zwei Komponenten:

  1. TextEditor.tsx: der Texteditor, den wir zuvor geschrieben haben.
  2. Preview.tsx: eine Komponente zum Anzeigen von Dateien im Markdown-Format.

Beim Erstellen dieser Seite verwenden wir unsere API:

  1. POST: Mit unserer Funktion postArticle senden wir den Artikel an den Server.

Wir werden auch den useSession-Hook verwenden, der von der Next-Auth-Bibliothek bereitgestellt wird, um das Authentifizierungstoken des Benutzers zu erhalten, das zur Registrierung des Artikels auf dem Server verwendet wird.

Dies umfasst drei verschiedene API-Aufrufe.
In app/pages/(private)/newArticle/page.tsx.

"Client verwenden";
import React, { ChangeEvent, useCallback, useState } from „react“;
import { useSession } from „next-auth/react“;
import { redirect } from „next/navigation“;
postArtical aus „@/services/postArticle“ importieren;
import { AiOutlineFolderOpen } aus „react-icons/ai“;
import { RiImageEditLine } aus „react-icons/ri“;

Bild aus „next/image“ importieren;
TextEditor aus „@/components/textEditor“ importieren;
Vorschau aus „@/components/PreviewText“ importieren;
import { AiOutlineSend } aus „react-icons/ai“;
import { BsBodyText } aus „react-icons/bs“;

Standardfunktion exportieren NewArticle(params:any) {
  const { data: session }: any = useSession({
    erforderlich: wahr,
    onUnauthenticated() {
      weiterleiten("/login");
    },
  });
  const [imageUrl, setImageUrl] = useState<object>({});
  const [previewImage, setPreviewImage] = useState<string>("");
  const [previewText, setPreviewText] = useState<boolean>(false);
  const [title, setTitle] = useState<string>("");
  const [doc, setDoc] = useState<string>("# Escreva o seu texto... n");
  const handleDocChange = useCallback((newDoc: any) => {
    setDoc(newDoc);
  }, []);

  if (!session?.user) return null;

  const handleArticleSubmit = async (e:any) => {
        e.preventDefault();
    const token: string = session.user.token;
    versuchen {
      const res = warte auf postArtical({
        id: session.user.userId.toString(),
        Token: Token,
        imageUrl: imageUrl,
        Titel: „Titel“,
        doc: doc,
      });
      console.log('re--->', res);
      umleiten('/success');
    } Catch (Fehler) {
      console.error('Fehler beim Senden des Artikels:', Fehler);
      // Fehler bei Bedarf behandeln
      Wurffehler;
    }
  };

  const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files.length > 0) {
      const file = e.target.files[0];
      const url = URL.createObjectURL(file);
      setPreviewImage(url);
      setImageUrl(file);
    }
  };

  const handleTextPreview = (e: any) => {
    e.preventDefault();
    setPreviewText(!previewText);
  };
  zurückkehren (
    <section className="w-full h-full min-h-screen relative py-8">
      {previewText && (
        <div className="absolute right-16 top-5 p-5 border-2 border-slate-500 bg-slate-100 abgerundet-xl w-full max-w-[33em] z-30">
          <Vorschau
            doc={doc}
            Titel={Titel}
            PreviewImage={previewImage}
            onPreview={() => setPreviewText(!previewText)}
          />
        </div>
      )}

      <form className="relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200 abgerundet-md bg-slate-50 Drop-Shadow-XL Flex Flex-Col Gap-2 ">
        {" "}
        <div className="flex justify-between items-center">
          <-Taste
            className="border-b-2 abgerundet-md border-slate-500 p-2 flexible items-center gap-2 hover:border-slate-400 hover:text-slate-800"
            onClick={handleTextPreview}
          >
            <BsBodyText />
            Vorschau
          </button>{" "}
          <-Taste
            className="group border border-b-2 border-slate-500 abgerundet-md p-2 flexible items-center gap-2 hover:border-slate-400 hover:text-slate-800 "
            onClick={handleArticleSubmit}
          >
            Senden Sie den Text
            <AiOutlineSend className="w-5 h-5 group-hover:text-red-500" />
          </button>
        </div>
        <div className="header-wrapper flex flex-col gap-2 ">
          <div className="image-box">
            {previewImage.length === 0 && (
              <div className="select-image">
                <Label
                  htmlFor="image"
                  className="p-4 border-dashed border-4 border-slate-400 Cursor-Pointer Flex Flex-Col Items-Center Justify-Center"
                >
                  <AiOutlineFolderOpen className="w-7 h-7" />
                  Drang-and-Drop-Bild
                </label>
                <Eingabe
                 >



<h4>
  
  
  Artikel bearbeiten
</h4>

<p>Eine Seite ähnlich wie <em>Neuer Artikel</em> (newArticle), mit einigen Unterschieden.</p>

<p>Zuerst definieren wir eine dynamische Route, bei der wir eine ID als Navigationsparameter erhalten. Dies ist sehr ähnlich zu dem, was auf der Seite zum Lesen von Artikeln durchgeführt wurde. <br>
app/(pages)/(private)/editArticle/[id]/page.tsx<br>
</p><pre class="brush:php;toolbar:false">"Client verwenden";
import React, { useState, useEffect, useCallback, useRef, ChangeEvent } from „react“;
import { useSession } from „next-auth/react“;
import { redirect } from „next/navigation“;
Bild aus „next/image“ importieren;

import { IArticle } from „@/interfaces/article.interface“;
import { AiOutlineEdit } aus „react-icons/ai“;
import { BsBodyText } aus „react-icons/bs“;
import { AiOutlineFolderOpen } aus „react-icons/ai“;
import { RiImageEditLine } aus „react-icons/ri“;

Vorschau aus „@/components/PreviewText“ importieren;
TextEditor aus „@/components/textEditor“ importieren;
import Loading from '@/components/Loading';
editArtical aus „@/services/editArticle“ importieren;

Standardfunktion exportieren EditArticle({ params }: { params: any }) {
 const { data: session }: any = useSession({
    erforderlich: wahr,
    onUnauthenticated() {
      weiterleiten("/login");
    },
  });
  const id: number = params.id;
  const [article, setArticle] = useState<IArticle | null>(null);
  const [imageUrl, setImageUrl] = useState<object>({});
  const [previewImage, setPreviewImage] = useState<string>("");
  const [previewText, setPreviewText] = useState<boolean>(false)
  const [title, setTitle] = useState<string>("");
  const [doc, setDoc] = useState<string>('');
  const handleDocChange = useCallback((newDoc: any) => {
    setDoc(newDoc);
  }, []);
  const inputRef= useRef<HTMLInputElement>(null);

  const fetchArticle = async (id: number) => {
    versuchen {
      const Antwort = Warten auf Abruf(
        `http://localhost:8080/articles/getById/${id}`,
      );
      const jsonData = waiting Response.json();
      setArticle(jsonData);
    } fangen (irrt) {
      console.log("etwas ist schiefgelaufen:", err);
    }
  };
  useEffect(() => {
    if (Artikel !== null || Artikel !== undefiniert) {
      fetchArticle(id);
    }
  }, [Ausweis]);

  useEffect(()=>{
    if(article != null && Article.content){
        setDoc(article.content)
    }

    if(article !=null && Article.image){
      setPreviewImage(`http://localhost:8080/` Article.image)
    }
  },[Artikel])

  const handleArticleSubmit = async (e:any) => {
     e.preventDefault();
    const token: string = session.user.token;
    versuchen{
      const res = warte auf editArtical({
      id: id,
      Token: Token,
      imageUrl:imageUrl,
      Titel: Titel,
      doc: doc,
      });
        console.log('re--->',res)
        Rückkehr res;
    } Catch(Fehler){
    console.log("Fehler:", Fehler)
    }
  };
  const handleImageClick = ()=>{
      console.log('hiii')
    if(inputRef.current){
      inputRef.current.click();
    }
  }const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files && e.target.files.length > 0) {
      const file = e.target.files[0];
      const url = URL.createObjectURL(file);
      setPreviewImage(url);
      setImageUrl(file);
    }

  };
   const handleTextPreview = (e: any) => {
    e.preventDefault();
    setPreviewText(!previewText);
    console.log('Hallo aus der Vorschau!')
  };

  if(!article) return <Loading/>
  if(Artikel?.Inhalt)
  zurückkehren (
    <section className='w-full h-full min-h-screen relative py-8'>
      {previewText && (
        <div className="absolute right-16 top-5 p-5 border-2 border-slate-500 bg-slate-100 abgerundet-xl w-full max-w-[33em] z-30">
          <Vorschau
            doc={doc}
            Titel={Titel}
            PreviewImage={previewImage}
            onPreview={() => setPreviewText(!previewText)}
          />
        </div>
      )}

      <div className='relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200 abgerundet-md bg-white drop- Shadow-MD Flex Flex-Col Gap-2'>
        <form className='relative mx-auto max-w-[700px] h-full min-h-[90%] w-full p-2 border-2 border-slate-200 abgerundet-md bg-slate-50 drop-shadow-md flex flex-col Gap-2 '>
          {" "}
          <div className='flex justify-between items-center'>
            <-Taste
              className='border-b-2 abgerundet-md border-slate-500 p-2 flexible items-center Gap-2 hover:border-slate-400 hover:text-slate-800'
              onClick={handleTextPreview}
            >
              <BsBodyText />
              Vorschau
            </button>{" "}
            <-Taste
              className='group border border-b-2 border-slate-500 abgerundet-md p-2 flexible items-center gap-2 hover:border-slate-400 hover:text-slate-800 '
              onClick={handleArticleSubmit}
            >
                Edite Artigo 
              <AiOutlineEdit className='w-5 h-5 group-hover:text-red-500' />
            </button>
          </div>
          <div className='header-wrapper flex flex-col gap-2 '>
            <div className='image-box'>
              {previewImage.length === 0 && (
                <div className='select-image'>
                  <Label
                    htmlFor='image'
                    className='p-4 border-dashed border-4 border-slate-400 Cursor-Pointer Flex Flex-Col Items-Center Justify-Center'
                  >
                    <AiOutlineFolderOpen className='w-7 h-7' />
                    Drang-and-Drop-Bild
                  </label>
                  <Eingabe
                   >



<h2>
  
  
  Abschluss
</h2>

<p>Zunächst möchte ich Ihnen dafür danken, dass Sie sich die Zeit genommen haben, dieses Tutorial zu lesen, und ich möchte Ihnen auch zum Abschluss gratulieren. Ich hoffe, es hat Ihnen gute Dienste geleistet und die Schritt-für-Schritt-Anleitung war leicht zu befolgen.</p>

<p>Zweitens möchte ich ein paar Punkte zu dem hervorheben, was wir gerade gebaut haben. Dies ist die Grundlage eines Blog-Systems, und es gibt noch viel hinzuzufügen, z. B. eine öffentliche Seite, auf der alle Artikel angezeigt werden, eine Benutzerregistrierungsseite oder sogar eine benutzerdefinierte 404-Fehlerseite. Wenn Sie sich während des Tutorials über diese Seiten gewundert haben und sie verpasst haben, wissen Sie, dass dies Absicht war. Dieses Tutorial hat Ihnen genügend Erfahrung vermittelt, um diese neuen Seiten selbst zu erstellen, viele andere hinzuzufügen und neue Funktionen zu implementieren.</p>

<p>Vielen Dank und bis zum nächsten Mal. o/</p>


          

            
        
Nach dem Login kopieren

Das obige ist der detaillierte Inhalt vonErstellen eines dynamischen Blog-Dashboards mit Next.js. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Neueste Artikel des Autors
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage