2krika
Getting Started

Quick Start

Guide de développement rapide pour 2Krika

Quick Start

Ce guide vous aide à démarrer rapidement le développement sur le projet 2Krika.

Workflow de Développement

1. Créer une Branche

# Créer une nouvelle branche depuis dev
git checkout dev
git pull origin dev
git checkout -b feature/nom-de-votre-feature

2. Démarrer le Serveur de Développement

# Depuis la racine du projet
pnpm dev

# Ou pour une app spécifique
cd apps/customers
pnpm dev

3. Faire vos Modifications

Suivez les conventions de code du projet et testez vos modifications localement.

4. Commit et Push

git add .
git commit -m "feat: description de votre feature"
git push origin feature/nom-de-votre-feature

5. Créer une Pull Request

Créez une PR vers la branche dev avec une description claire de vos changements.

Structure d'une Feature

Exemple: Ajouter une Nouvelle Page

Pour l'app customers avec routing internationalisé:

apps/customers/src/app/[lang]/(client)/nouvelle-page/page.tsx
import { useTranslations } from 'next-intl';

export default function NouvellePage() {
  const t = useTranslations('NouvellePage');

  return (
    <div>
      <h1>{t('title')}</h1>
      <p>{t('description')}</p>
    </div>
  );
}

Ajouter les traductions:

apps/customers/src/locales/fr.json
{
  "NouvellePage": {
    "title": "Nouvelle Page",
    "description": "Description de la page"
  }
}
apps/customers/src/locales/en.json
{
  "NouvellePage": {
    "title": "New Page",
    "description": "Page description"
  }
}

Exemple: Créer un Composant Réutilisable

apps/customers/src/components/UserCard.tsx
import { Card, Avatar, Text } from '@mantine/core';
import { User } from '@/models/user';

interface UserCardProps {
  user: User;
  onClick?: () => void;
}

export function UserCard({ user, onClick }: UserCardProps) {
  return (
    <Card onClick={onClick} className="cursor-pointer hover:shadow-lg">
      <Avatar src={user.avatar} size="lg" />
      <Text size="lg" fw={600}>{user.name}</Text>
      <Text size="sm" c="dimmed">{user.email}</Text>
    </Card>
  );
}

Exemple: Créer un Hook Personnalisé

apps/customers/src/hooks/useUser.ts
import { useQuery } from '@tanstack/react-query';
import { userService } from '@/services/userService';

export function useUser(userId: string) {
  return useQuery({
    queryKey: ['user', userId],
    queryFn: () => userService.getUser(userId),
    enabled: !!userId,
  });
}

Exemple: Créer un Service API

apps/customers/src/services/userService.ts
import api from '@/config/api';
import { User } from '@/models/user';

export const userService = {
  getUser: async (userId: string): Promise<User> => {
    const { data } = await api.get(`/users/${userId}`);
    return data;
  },

  updateUser: async (userId: string, userData: Partial<User>): Promise<User> => {
    const { data } = await api.put(`/users/${userId}`, userData);
    return data;
  },
};

Travailler avec React Query

Récupérer des Données

'use client';

import { useQuery } from '@tanstack/react-query';
import { serviceService } from '@/services/serviceService';

export function ServicesList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['services'],
    queryFn: serviceService.getAll,
  });

  if (isLoading) return <div>Chargement...</div>;
  if (error) return <div>Erreur: {error.message}</div>;

  return (
    <div>
      {data?.map(service => (
        <ServiceCard key={service.id} service={service} />
      ))}
    </div>
  );
}

Mutations

'use client';

import { useMutation, useQueryClient } from '@tanstack/react-query';
import { serviceService } from '@/services/serviceService';

export function CreateServiceForm() {
  const queryClient = useQueryClient();

  const mutation = useMutation({
    mutationFn: serviceService.create,
    onSuccess: () => {
      // Invalider et refetch
      queryClient.invalidateQueries({ queryKey: ['services'] });
    },
  });

  const handleSubmit = (data: CreateServiceData) => {
    mutation.mutate(data);
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* Form fields */}
    </form>
  );
}

Travailler avec Zustand

Créer un Store

apps/customers/src/stores/authStore.ts
import { create } from 'zustand';
import { User } from '@/models/user';

interface AuthState {
  user: User | null;
  token: string | null;
  setUser: (user: User) => void;
  setToken: (token: string) => void;
  logout: () => void;
}

export const useAuthStore = create<AuthState>((set) => ({
  user: null,
  token: null,
  setUser: (user) => set({ user }),
  setToken: (token) => set({ token }),
  logout: () => set({ user: null, token: null }),
}));

Utiliser le Store

'use client';

import { useAuthStore } from '@/stores/authStore';

export function UserProfile() {
  const user = useAuthStore((state) => state.user);
  const logout = useAuthStore((state) => state.logout);

  if (!user) return null;

  return (
    <div>
      <h2>{user.name}</h2>
      <button onClick={logout}>Se déconnecter</button>
    </div>
  );
}

Styling avec Tailwind

Classes Utilitaires

<div className="flex items-center justify-between p-4 bg-white rounded-lg shadow-md">
  <h2 className="text-2xl font-bold text-gray-900">Titre</h2>
  <button className="px-4 py-2 text-white bg-blue-600 rounded hover:bg-blue-700">
    Action
  </button>
</div>

Responsive Design

<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
  {/* Cards */}
</div>

Composants Mantine

Utilisation de Base

import { Button, TextInput, Modal } from '@mantine/core';

export function MyForm() {
  return (
    <>
      <TextInput
        label="Email"
        placeholder="votre@email.com"
        required
      />
      <Button type="submit">Envoyer</Button>
    </>
  );
}

Formulaires avec Mantine

'use client';

import { useForm } from '@mantine/form';
import { TextInput, Button } from '@mantine/core';

export function ContactForm() {
  const form = useForm({
    initialValues: {
      email: '',
      name: '',
    },
    validate: {
      email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Email invalide'),
    },
  });

  return (
    <form onSubmit={form.onSubmit(console.log)}>
      <TextInput
        label="Nom"
        {...form.getInputProps('name')}
      />
      <TextInput
        label="Email"
        {...form.getInputProps('email')}
      />
      <Button type="submit">Envoyer</Button>
    </form>
  );
}

Socket.io pour Real-time

Connexion Socket

'use client';

import { useEffect, useState } from 'react';
import { io, Socket } from 'socket.io-client';

export function useSocket() {
  const [socket, setSocket] = useState<Socket | null>(null);

  useEffect(() => {
    const socketInstance = io(process.env.NEXT_PUBLIC_SOCKET_URL!);
    setSocket(socketInstance);

    return () => {
      socketInstance.disconnect();
    };
  }, []);

  return socket;
}

Écouter les Événements

'use client';

import { useEffect } from 'react';
import { useSocket } from '@/hooks/useSocket';

export function ChatComponent() {
  const socket = useSocket();

  useEffect(() => {
    if (!socket) return;

    socket.on('message', (data) => {
      console.log('Nouveau message:', data);
    });

    return () => {
      socket.off('message');
    };
  }, [socket]);

  return <div>Chat Component</div>;
}

Tests

Tester un Composant

import { render, screen } from '@testing-library/react';
import { UserCard } from './UserCard';

describe('UserCard', () => {
  it('affiche le nom de l\'utilisateur', () => {
    const user = { id: '1', name: 'John Doe', email: 'john@example.com' };
    render(<UserCard user={user} />);
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });
});

Commandes Utiles

# Vérifier les types
pnpm check-types

# Linter le code
pnpm lint

# Formater le code
pnpm format

# Build pour la production
pnpm build

# Nettoyer les caches
rm -rf .next node_modules
pnpm install

Debugging

Logs dans le Navigateur

console.log('Debug:', data);
console.error('Error:', error);

React DevTools

Installez l'extension React DevTools pour Chrome/Firefox pour inspecter les composants.

Network Tab

Utilisez l'onglet Network dans les DevTools pour inspecter les requêtes API.

Prochaines Étapes

On this page