Frontend workflow
Πώς αναπτύσσεται ο React/TypeScript/Vite client χωρίς να αντιγράφει το domain ownership του backend.
Αρμοδιότητες χρόνου εκτέλεσης
Το frontend ανήκει:
- οδοί και ναυσιπλοΐα·
- διάταξη, τυπογραφία, συμπεριφορά απόκρισης και σύνθεση υλικού UI·
- κατάσταση φόρμας και ανάδραση χρήστη·
- ανάκτηση και ακύρωση της κατάστασης εξυπηρετητή μέσω TanStack Query·
- προσάρτηση των JWT και χειρισμός μίας πρόσβασης-token ανανέωση μέσω Axios;
- παρουσίαση σφαλμάτων επικύρωσης API και άδειας.
Το frontend δεν ανήκει:
- την οργάνωση των αποφάσεων συμμετοχής ή ρόλου·
- tenant scoping κατά τη διάρκεια των εγγραφών·
- CRM επικύρωση που πρέπει επίσης να προστατεύσει μη-browser πελάτες?
- εκτέλεση αυτοματοποίησης·
- επιμονή πρόσβασης ή ανανέωση σημείων.
Κανόνας αίτησης: αγκίστρια στο όριο React
Τα αιτήματα που χρησιμοποιούνται από το React πρέπει να εκτίθενται ως άγκιστρα. Αυτή είναι μια καλή πρακτική εδώ, επειδή δίνει σε κάθε σελίδα την ίδια cache, φόρτωση, σφάλμα, ακύρωση, και συμπεριφορά authenticationς. Διατηρεί επίσης τα URL και τους τύπους απόκρισης έξω από τα οπτικά συστατικά.
Τα στρώματα είναι σκόπιμα ξεχωριστά:
page/component → feature hook → feature service → services/api.ts → Axiosservices/api.ts είναι το χαμηλό επίπεδο μεταφοράς: γνωρίζει Axios, κεφαλίδες Bearer, ανανέωση, και API λάθη. Ο φάκελος services/ ενός χαρακτηριστικού κατέχει τις ωμές κλήσεις του τελικού σημείου. Ο φάκελος hooks/ έχει τα κλειδιά ερωτημάτων, τους ορισμούς TanStack Query και τα άγκιστρα React. Οι κοινόχρηστες διεπαφές τομέα ζουν στο types/.
Για παράδειγμα:
const companies = useCompanies(organizationId)Μια σελίδα δεν πρέπει να περιέχει request('/organizations/...'), να δημιουργήσει μια περίπτωση Axios, ή να διαχειριστεί χειροκίνητα isLoading και να ακυρώσει την λανθάνουσα μνήμη για ένα αίτημα διακομιστή. Το Login/logout εξακολουθεί να αποτελεί σαφή εξαίρεση, επειδή είναι ενέργειες auth ενορχήστρωσης· ζουν στο features/auth/hooks/useAuth.tsx και χρησιμοποιούν την υπηρεσία auth.
Δείτε τους Frontend Rules για το checklist που εφαρμόζεται στο repository.
Χάρτης καταλόγου
frontend/src/
├── components/
│ ├── common/ reusable dialogs and feedback states
│ └── layout/ authenticated application shell
├── config/ TanStack Query client and app configuration
├── context/ shared React contexts
├── features/
│ ├── auth/ hooks, services, Zustand store
│ ├── organizations/ domain hooks and services
│ ├── companies/ domain hooks and services
│ ├── contacts/ domain hooks and services
│ ├── automations/ domain hooks and services
│ ├── invitations/ invitation hooks and services
│ └── organizations/ workspace, settings, and email account hooks/services
├── hooks/ globally reusable hooks
├── pages/ route-level screens grouped by page
├── routes/ TanStack Router configuration
├── services/ Axios client and shared external services
├── styles/ Material UI theme
└── types/ shared TypeScript domain interfacesΠώς λειτουργεί η authentication
Σύνδεση είναι μια ροή δύο αιτημάτων:
POST /api/v1/auth/token/με όνομα χρήστη και κωδικό πρόσβασης.- Αποθήκευση πρόσβασης και ανανέωση σημείων σε Zustand με την ίδια-tab
sessionStorageεπιμονή. GET /api/v1/auth/me/με το access token.- Αποθηκεύστε τον επιστρεφόμενο χρήστη και τους οργανισμούς στο πλαίσιο auth / state.
Axios προσθέτει το access token σε κανονικές αιτήσεις API. Σε ένα 401 κάνει ένα POST /api/v1/auth/token/refresh/ αίτημα. Τα τρέχοντα αποτυχημένα αιτήματα μοιράζονται μία υπόσχεση ανανέωσης, η οποία αποτρέπει μια έκρηξη των ληξιπρόθεσμων αιτημάτων από την περιστροφή του refresh token πολλές φορές. Μια επιτυχής περιστροφή αντικαθιστά και τα δύο σημεία? αποτυχία καθαρίζει τη συνεδρία.
Η αποσύνδεση δημοσιεύει το refresh token σε /api/v1/auth/logout/, κατόπιν καθαρίζει το Zustand και την κρύπτη TanStack Query ακόμα και αν η κλήση του εξυπηρετητή αποτύχει.
Τα σημεία επιμένουν μόνο για την τρέχουσα καρτέλα browser μέσω του καταστήματος Zustand. Ένας μελλοντικός σχεδιασμός HttpOnly-cookie πρέπει να είναι μια ρητή απόφαση ασφάλειας και να τεκμηριώνεται ως αλλαγή σύμβασης API/browser.
Θέμα και προσαρμογή της μάρκας
Η εφαρμογή έχει ένα Material UI ThemeProvider σε main.tsx. Οι προγραμματιστές προσαρμόζουν το μικρό σύνολο εσκεμμένων εισροών στο src/styles/brand.ts:
- χρώματα πρωτογενούς και δευτερογενούς μάρκας·
- φόντο εφαρμογής και επιφάνεια·
- πρωτογενές και σιωπηλό κείμενο·
- οικογένεια γραμματοσειράς·
- ακτίνα των συνόρων βάσης.
src/styles/theme.ts αντλεί το φως / σκοτεινό πρωτογενείς παραλλαγές, διαχωριστές, καταστάσεις δράσης, στυλ πίνακα, και κοινές προεπιλεγμένες συνιστώσες. Σελίδες και συστατικά χρησιμοποιούν σημασιολογική παλέτα μονοπάτια αντί για χρώμα κυριολεκτικά. Αυτό επιτρέπει σε μια αυτο-φιλοξενούμενη ομάδα να αντικαταστήσει την παλέτα βάσης χωρίς να ψάξει μέσα από την εφαρμογή.
Οι αναλυτικοί κανόνες για τα contracts και τα proxy modules βρίσκονται στο src/styles/README.md.
Προσθήκη σελίδας μόνο για ανάγνωση
- Προσθήκη ή ενημέρωση του τύπου απόκρισης στο
src/types/. - Προσθέστε το ακατέργαστο endpoint κλήσης στο φάκελο
services/της λειτουργίας. - Προσθέστε το κλειδί ερωτήματος,
queryOptions, και συνδέστε στο φάκελοhooks/της λειτουργίας. - Συμπεριλάβετε το ID οργάνωσης τόσο στο URL όσο και το query key για τα tenant-scoped δεδομένα.
- Προσθέστε ένα όνομα
*Page.tsxυπόsrc/pages/<PageName>/. - Προσθέστε τη διαδρομή του στο
src/routes/router.tsxκαι την καταχώρηση πλοήγησης στη διάταξη. - Φόρτωση χειρολαβών, κενό, σφάλμα και επιτυχημένες καταστάσεις.
- Προσθέστε μια δοκιμή browser με μια χλευασμένη απάντηση API.
Παράδειγμα σχήματος ερωτήματος:
export const companyKeys = {
byOrganization: (organizationId: string) =>
['organizations', organizationId, 'companies'] as const,
}
const companiesQuery = (organizationId: string) => queryOptions({
queryKey: companyKeys.byOrganization(organizationId),
queryFn: ({ signal }) => companyService.list(organizationId, signal),
})
export const useCompanies = (organizationId: string) =>
useQuery(companiesQuery(organizationId))Ο ορισμός του ερωτήματος μπορεί να παραμείνει ιδιωτικός. Εξαγωγή του εργοστασίου hook και κλειδί μέσω του index.ts της λειτουργίας. Οι σελίδες διαδρομής εισάγονται απευθείας από τον δρομολογητή έτσι τεμπέληδες φόρτωση δεν ηττάται από ένα χαρακτηριστικό βαρέλι που εισάγονται αλλού.
Προσθήκη φόρμας δημιουργίας
Χρήση του κοινόχρηστου CreateDialog όταν η φόρμα είναι απλή POST:
- Αποδώστε τη φόρμα μόνο όταν το επιτρέπει η τρέχουσα ικανότητα.
- Αντιμετώπισε αυτή την ορατότητα ως UX, όχι ως ασφάλεια.
- Αποστολή μόνο εγγράψιμων πεδίων; ποτέ μην στείλετε έναν οργανισμό ID που παρέχεται από ένα Επιλογέας workspace.
- Εμφάνιση σφαλμάτων επικύρωσης επιπέδου πεδίου API.
- Ακυρώστε το επηρεαζόμενο ερώτημα μετά από απάντηση 2xx.
- Δοκιμάστε τόσο την επιτυχία όσο και μια αντιπροσωπευτική 400 απάντηση.
Το backend παραμένει έγκυρο όταν ένας θεατής καλεί το endpoint άμεσα.
Τοπικές εντολές
Από το frontend/:
npm ci
npm run dev
npm run lint
npm test
npm run build
npm run test:e2eΗ σουίτα browser χλευάζει την κίνηση /api/v1/. Ελέγχει τη συμπεριφορά UI και το tenant isolation χωρίς να απαιτεί έναν τρέχοντα εξυπηρετητή Django.
Συμβάσεις TanStack Router
Οι διαδρομές ορίζονται στο src/routes/router.tsx με το δέντρο διαδρομής που βασίζεται στον κώδικα TanStack Router. Η βασική διαδρομή κατέχει το κέλυφος, η διαδρομή του οργανισμού κατέχει την παράμετρο $organizationId, και τα παιδικά δρομολόγια έχουν δική τους επισκόπηση, εταιρείες, επαφές και οθόνες automation.
Χρήση προτύπων διαδρομής και παραμέτρων για δυναμική πλοήγηση:
<Link
to="/organizations/$organizationId/companies"
params={{ organizationId: organization.id }}
>
Companies
</Link>Αυτό είναι προτιμότερο από τη σύνθεση URL ως απλού string, επειδή το Router ελέγχει τους τύπους της διαδρομής και των απαιτούμενων parameters. Αν το polymorphic prop component={Link} μιας component library δεν υποστηρίζει τα generics του TanStack Router, τοποθετήστε το οπτικό component μέσα σε ένα TanStack Link με σωστούς τύπους και κρατήστε το child component μη διαδραστικό.
Οι διαδρομές workspace χρησιμοποιούν το beforeLoad για να ανακατευθύνουν χρήστες που δεν έχουν συνδεθεί στη δημόσια αρχική διαδρομή. Το backend εξακολουθεί να ελέγχει το authorization όταν ζητούνται δεδομένα workspace· ο route guard εξυπηρετεί μόνο την πλοήγηση και το UX.
