Tutti Progetti

Psicoterapeuta Blog: Sito Web e piccolo Blog

Sep 1
Sito web psicoterapeuta

Sito Web: https://graziadecarlo.it/

Il sito web https://graziadecarlo.it/ rappresenta una vetrina per una psicoterapeuta per presentare in modo chiaro e professionale i servizi offerti. Ogni servizio è descritto in dettaglio per fornire agli utenti una panoramica completa delle competenze della professionista.

Un aspetto fondamentale del progetto è stata l’implementazione di una sezione blog, pensata per accogliere articoli tematici della psicoterapeuta. Grazie a un sistema di gestione dei contenuti personalizzato (CMS), accessibile tramite l'autenticazione Google, è possibile gestire il blog autonomamente anche senza competenze tecniche. Questo approccio mantiene i contenuti aggiornati, contribuendo a stabilire un dialogo continuo con i visitatori interessati.

Stack Tecnologico

Lo sviluppo del sito ha previsto l’utilizzo di un set di tecnologie moderne e consolidato. Le scelte tecnologiche sono state guidate da tre obiettivi principali:

La scelta è ricaduta principalmente su queste librerie Next.js, Material UI, Prisma e Typescript.

Next.js

Next.js è un framework basato su React che semplifica la configurazione e ottimizza le prestazioni. Tra i vantaggi che ho trovato:

Grazie a Next.js, posso sviluppare applicazioni più efficaci e scalabili, che sarebbero altrimenti difficili da mantenere come sviluppatore singolo.

TypeScript

TypeScript è uno strumento potente che aggiunge il supporto dei tipi statici a JavaScript, consentendo di rilevare errori durante lo sviluppo. Con TypeScript:

Prisma

Prisma è un ORM (Object-Relational Mapping) che facilita la gestione e l'interrogazione del database con un'interfaccia più intuitiva rispetto alla scrittura di query SQL tradizionali.

Grazie a Prisma, posso concentrarmi di più sulla logica del progetto e meno sulla gestione complessa del database, il che è essenziale per lo sviluppo rapido e sicuro.

Material UI

Material UI è una libreria di componenti React che si basa sulle linee guida di Material Design di Google.

In particolare, Material UI mi ha aiutato a creare rapidamente interfacce professionali, risparmiando tempo nello sviluppo del design e permettendomi di concentrarmi maggiormente sulla funzionalità del sito.

NextAuth

Note: per installare npm install next-auth

NextAuth è una libreria open-source per Next.js che semplifica l'implementazione dell'autenticazione nelle applicazioni web.

Supporta diversi provider di autenticazione, come Google, Facebook, GitHub e molti altri, oltre ad offrire un'integrazione semplice per email e credenziali.

Con NextAuth.js è possibile gestire facilmente login, logout, sessioni e autorizzazioni, rendendo più sicuro e veloce il processo di autenticazione degli utenti.

Cloudinary

Note: per installare npm install cloudinary

Cloudinary è una piattaforma per la gestione di immagini e video su web e mobile. Offre strumenti per caricare, archiviare, ottimizzare e trasformare contenuti multimediali in tempo reale, migliorando le prestazioni e l’esperienza utente.

Cloudinary supporta funzionalità avanzate come il ridimensionamento automatico, la compressione, il formato adattivo e la distribuzione tramite CDN, rendendolo ideale per sviluppatori e aziende che vogliono gestire contenuti visivi in modo efficiente.

Lexical

Note: per installare npm install --save lexical @lexical/react

Lexical è un editor di testo open-source basato su React, progettato per essere altamente estensibile, performante e accessibile. Fornisce agli sviluppatori la flessibilità di creare esperienze di editing avanzate con supporto per plugin personalizzati, gestione della selezione e rendering ottimizzato.

Lexical è ideale per applicazioni che richiedono editor di testo avanzati, come CMS, piattaforme di blogging e app di collaborazione.

Design

Il design, in linea con le preferenze della cliente, utilizza come colore principale un verde scuro, con i font Cormorant e Roboto. Ho realizzato un video introduttivo e un logo in SVG per facilitare la personalizzazione CSS di colore e animazione.

theme
overrides
Components.ts
Typography.ts
typography.ts
index.ts

Il tema di Material UI è stato costruito con un’architettura modulare, suddividendo le componenti (come Typography.ts per i testi e Components.ts per gli elementi) e unendo tutto nel file index.ts. In questo modo è stato possibile esportare il tema per passarlo al <ThemeProvider theme={theme} /> nel file di layout.ts.

./theme/index.ts
import { createTheme, ThemeOptions } from "@mui/material/styles";
import typography from "./typography";
import ComponentsOverrides from "./overrides";
 
const themeOptions: ThemeOptions = {
  palette: {
    mode: "light",
    primary: {
      main: "#065F46",
    },
    warning: {
      main: "#C92A2A",
    },
  },
  typography,
  direction: "ltr",
};
 
const theme = createTheme(themeOptions);
theme.components = ComponentsOverrides(theme);
 
export default theme;
app
(auth)
layout.tsx
(client)
layout.tsx

Per differenziare il layout dell'area utente rispetto all'accesso al CMS, ho definito due layout root distinti, eliminando il layout principale presente nella directory ./app:.

Ho quindi configurato un layout dedicato per la sezione di autenticazione e gestione utente all'interno di /(auth)/layout.ts, dove sono state definite tutte le componenti necessarie per la login e per le funzionalità dell'utente autenticato, evitando di caricare elementi non necessari per il client pubblico.

Animazioni

Per ottenere un effetto di comparsa graduale dei vari componenti nella pagina, ho implementato un'animazione CSS e un hook di osservazione dell'intersezione, che permette di rilevare quando un elemento entra nell'area visibile. Questo approccio migliora l’esperienza visiva, rendendo il caricamento dei componenti più fluido.

Seleziona file
./globals.scss
@keyframes compFadeIn {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
 
.componentsFadeIn {
animation: compFadeIn 1s ease-in-out forwards;
}
 

Grazie a questa configurazione, i componenti come CardPost si animano gradualmente quando diventano visibili, migliorando l'estetica della pagina e rendendo l'interfaccia utente più interattiva e coinvolgente.

Autenticazione

L'autenticazione è implementata per permettere l’accesso solo all’account della psicoterapeuta come amministratore, grazie alla libreria NextAuth:

lib/auth.ts
import { AuthOptions } from "next-auth";
import GoogleProvider from "next-auth/providers/google";
import { PrismaAdapter } from "@auth/prisma-adapter";
import { Adapter } from "next-auth/adapters";
import prisma from "@/lib/prismadb";
 
const adminEmail = "xxxxx@yyyy.com"; // L'email del tuo utente admin
 
export const authOptions: AuthOptions = {
  adapter: PrismaAdapter(prisma) as Adapter,
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
  ],
  secret: process.env.NEXTAUTH_SECRET,
  callbacks: {
    async signIn({ user, account }) {
      // Permetti solo l'accesso all'utente con l'email specificata
      const { name, email } = user;
      if (email === adminEmail) {
        return true;
      } else {
        return false;
      }
    },
    async jwt({ token, user }) {
      if (user) {
        token.id = user.id;
        token.name = user.name;
        token.email = user.email;
      }
      return token;
    },
    async session({ session, token, user }: any) {
      if (token) {
        session.user.name = token.name;
        session.user.email = token.email;
      }
      return session;
    },
  },
};

Database

Il database, costruito con MongoDB e Prisma, memorizza post e dati utente. L’architettura è pensata per gestire post esclusivamente creati dall’utente amministratore.

./prisma/schema.prisma
datasource db {
  provider = "mongodb"
  url      = env("DATABASE_URL")
}
 
generator client {
  provider = "prisma-client-js"
}
 
model User {
  id            String    @id @default(cuid()) @map("_id")
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  Blog          Blog[]
}
 
model Blog {
  id        String   @id @default(cuid()) @map("_id")
  createdAt DateTime @default(now())
  updatedAt DateTime @default(now())
  slug      String  @unique
  title     String
  desc      String
  img       String?
  imgId     String?
  userEmail String
  published  Boolean  @default(false)
  topPost   Boolean  @default(false)
  user      User     @relation(fields: [userEmail], references: [email])
}
 

L'utilizzo di Prisma ha semplificato e velocizzato la gestione delle operazioni di accesso alle risorse, rendendo le operazioni CRUD (Create, Read, Update, Delete) più efficienti e sicure. In particolare, Prisma permette di lavorare con un'API intuitiva per il database, riducendo il rischio di errori e aumentando la produttività. Per esempio, di seguito troviamo un’implementazione delle azioni per recuperare i post del blog e ottenere i dettagli di un singolo post tramite il suo slug.

./app/(auth)/actions/blogActions.ts
export async function getPosts() {
  const posts = await prisma.blog.findMany({
    include: {
      user: true,
    },
  });
 
  return posts;
}
 
export async function getPost(slug: string): Promise<Blog | null> {
  const post = await prisma.blog.findUnique({
    where: {
      slug: slug as string,
    },
  });
 
  return post;
}

Questo mi ha permesso di avere:

Salvataggio immagini

Per la gestione delle immagini, ho scelto Cloudinary per le sue potenti funzionalità di ottimizzazione e gestione dei contenuti visivi, che contribuiscono a migliorare la velocità di caricamento del sito e la qualità dell’esperienza utente. Cloudinary supporta trasformazioni dinamiche delle immagini, permettendomi di applicare ridimensionamenti e compressioni al momento del caricamento senza sacrificare la qualità.

Nell’applicazione, ho implementato una funzione per il caricamento delle immagini che gestisce l’interazione con l'API di Cloudinary, caricando ogni immagine su un server esterno e restituendo un URL ottimizzato per il web:

Note: utilizzo come immagine di prova per la copertina del post, il backround del mio sito web.

Sito web psicoterapeuta - nuovo post

Grazie a questa funzione, gli utenti possono caricare le immagini in modo rapido e sicuro. Inoltre, ogni immagine è ottimizzata automaticamente e memorizzata nella cartella specifica del progetto per una gestione ordinata dei contenuti.

./app/(auth)/actions/blogActions.ts
async function uploadImage(file: Blob) {
  return new Promise<UploadApiResponse>(async (resolve, reject) => {
    const buffer = Buffer.from(await file.arrayBuffer());
    v2.uploader
      .upload_stream(
        {
          resource_type: "auto",
          folder: "nomecartella",
        },
        (err, result) => {
          if (err) {
            console.err("Error image: ", err);
            return reject(err);
          } else if (result) {
            return resolve(result);
          }
        }
      )
      .end(buffer);
  });
}

Dopo il salvataggio del post andato a buon fine, verrà salvato il post sul mongo e visualizzato nella pagina di dashboard con la possibilità di eliminarlo e/o modificarlo.

Sito web psicoterapeuta - nuovo post

Editor di Testo Avanzato

Considerando che il cliente non ha una conoscenza tecnica, ho selezionato un editor di testo avanzato che consente di scrivere e formattare i post in modo semplice e intuitivo. Dopo un’attenta ricerca, ho optato per Lexical, una libreria flessibile che offre la possibilità di creare plugin personalizzati per migliorare l’esperienza utente e adattarsi alle esigenze specifiche del cliente.

Sito web psicoterapeuta - editor nuovo post

Funzionalità dell'Editor

L'editor Lexical permette di costruire una toolbar personalizzata e aggiungere funzionalità specifiche grazie ai plugin, rendendo l'interfaccia di scrittura personalizzabile. Inoltre, il testo formattato viene salvato in editorState, una variabile JSON che conserva la struttura del contenuto, rendendo la gestione dei post semplice e sicura.

L'implementazione include un plugin toolbar per le principali azioni di formattazione (grassetto, corsivo, link, ecc.) e un plugin di gestione dello stato in tempo reale, che aggiorna automaticamente editorState ogni volta che l'utente effettua modifiche. Di seguito, la configurazione e il codice dell’editor:

./components/core/Editor/Editor.tsx
const Editor = ({ name, desc }: EditorProps) => {
  const [editorState, setEditorState] = useState<string>("");
 
  function onChange(editorState) {
    const editorStateJSON = editorState.toJSON();
    setEditorState(JSON.stringify(editorStateJSON));
  }
 
  const editorConfig = {
    namespace: "myEditor",
    editorState: desc?.length > 0 ? desc : null,
    onError(error: Error) {
      throw error;
    },
    theme: Theme,
  };
 
  return (
    <LexicalComposer initialConfig={editorConfig}>
      <div className={styles["editor-container"]}>
        <ToolbarPlugin />
        <div className={styles["editor-inner"]}>
          <RichTextPlugin
            contentEditable={
              <ContentEditable
                className={styles["editor-input"]}
                aria-placeholder={placeholder}
                placeholder={
                  <div className={styles["editor-placeholder"]}>
                    {placeholder}
                  </div>
                }
              />
            }
            ErrorBoundary={LexicalErrorBoundary}
          />
          <HistoryPlugin />
          <AutoFocusPlugin />
          <OnChangePlugin onChange={onChange} />
        </div>
      </div>
      <input type="hidden" value={editorState} name={name} />
    </LexicalComposer>
  );
};
export default Editor;

Grazie a questa soluzione, la psicoterapeuta può aggiornare i contenuti in autonomia, formattando il testo secondo le necessità e ottenendo un'interfaccia intuitiva che semplifica la gestione del blog senza la necessità di conoscere linguaggi di markup.

SEO e Sitemap

Per l'ottimizzazione SEO, ho integrato robots.txt e sitemap.xml, generata dinamicamente con Next.js. Infatti NextJs permette di generare dinamicamente la sitemap per tutte le pagine del sito, garantendo che ogni nuovo articolo sia automaticamente aggiunto e indicizzato. Visitando il sito online sarà possibile vedere la sitemap generata.

sitemap.xml
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://graziadecarlo.it</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>yearly</changefreq>
    <priority>1</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/contattami</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/grazia-decarlo</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/servizi</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/terapia-individuale</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/terapia-coppia</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/terapia-EMDR</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/sviluppo-emotivo-bambino-adolescente</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/consulenza-genitoriale</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
  <url>
    <loc>https://graziadecarlo.it/disturbi-apprendimento</loc>
    <lastmod>2024-09-08T12:53:56.894Z</lastmod>
    <changefreq>monthly</changefreq>
    <priority>0.8</priority>
  </url>
</urlset>

Sono stati inseriti i principali Metadati nella intestazione come il titolo, descrizione, immagine per twitter e opengraph, keywords ,ecc...

Con l'uso di Google Search Console è possibile avere una panoramica di dati utili come ad esempio le query più utilizzate dagli utenti per trovare il sito sviluppato.

Sito web psicoterapeuta - nuovo post

Vercel

Vercel è una piattaforma di deployment e collaborazione per sviluppatori frontend integrata con GitHub, che supporta un flusso di lavoro fluido tra sviluppo e rilascio. Nel mio caso, gestisco due branch principali:

Seguo una numerazione del tipo "X.X.X-SNAPSHOT", dove:

Quando le modifiche passano su main, il suffisso -SNAPSHOT viene rimosso per indicare che la versione è stabile e pronta per la distribuzione.