dodalem pinia store.

dodalem dokumentacje opisujaca dzialanie store
dodalem services do komunikacji z directus
dodalem opis dzialania services
dodalem mocki danych
przygotowania do refaktoryzacji
This commit is contained in:
2026-04-30 16:35:53 +02:00
parent 5725c024dc
commit 6e3d722e69
19 changed files with 3286 additions and 29 deletions

502
notes/api_services.md Normal file
View File

@@ -0,0 +1,502 @@
# API Services I Mocki
Data: 2026-04-29
## Cel dokumentu
Ten dokument opisuje warstwe API we frontendzie.
Warstwa API znajduje sie w:
```txt
frontend/src/services/
```
Jej zadaniem jest odseparowanie store od szczegolow komunikacji HTTP.
Store nie powinien wiedziec:
- jaki dokladnie jest URL endpointu,
- czy komunikacja idzie przez Axios,
- czy dane pochodza z prawdziwego backendu,
- czy dane pochodza z mockow JSON.
Store powinien wywolac funkcje domenowa, np.:
```js
fetchProducts(params)
```
a service powinien zajac sie reszta.
## Aktualne pliki services
```txt
frontend/src/services/apiMode.js
frontend/src/services/productsApi.js
frontend/src/services/productTimelineApi.js
frontend/src/services/productSpecificationApi.js
frontend/src/services/dictionariesApi.js
```
## Przeplyw danych
Docelowy przeplyw wyglada tak:
```txt
komponent Vue
-> store Pinia
-> service API
-> mock JSON albo Axios
-> backend Directus
```
Przyklad:
```txt
IndexPage.vue
-> productsStore.fetchFirstPage()
-> fetchProducts(params)
-> products.json albo GET /mayo-api/products
```
## Dlaczego services sa osobna warstwa
Bez services store musialby robic cos takiego:
```js
api.get('/mayo-api/products', { params })
```
Wtedy store zna:
- Axios,
- endpoint,
- strukture odpowiedzi HTTP.
To miesza odpowiedzialnosci.
Po wydzieleniu services store robi:
```js
const { items, pageInfo } = await fetchProducts(params)
```
To jest lepsze, bo:
- store skupia sie na stanie aplikacji,
- services skupiaja sie na komunikacji,
- endpoint mozna zmienic w jednym miejscu,
- latwiej dodac mocki,
- latwiej testowac logike store.
## `apiMode.js`
Plik:
```txt
frontend/src/services/apiMode.js
```
Zawiera przelacznik miedzy mock API i prawdziwym API:
```js
export const USE_MOCK_API = import.meta.env.VITE_USE_MOCK_API !== 'false'
```
To oznacza:
- domyslnie mocki sa wlaczone,
- jesli ustawisz `VITE_USE_MOCK_API=false`, aplikacja uzyje prawdziwego backendu.
Uruchomienie z prawdziwym backendem:
```bash
VITE_USE_MOCK_API=false npm run dev
```
Uruchomienie z mockami:
```bash
npm run dev
```
Mocki maja sztuczne opoznienie:
```js
waitForMockApi()
```
Dzieki temu podczas pracy nad UI widac stany ladowania podobne do prawdziwego API.
## Pliki mockow
Mocki sa zwyklymi plikami JSON:
```txt
frontend/src/mocks/products.json
frontend/src/mocks/timelines.json
frontend/src/mocks/specifications.json
frontend/src/mocks/dictionaries.json
```
Sa uzywane tylko przez warstwe `services`.
Komponenty i store nie importuja mockow bezposrednio.
To jest wazne, bo komponent nie powinien wiedziec, skad przyszly dane. Dla komponentu dane z mocka i z backendu powinny wygladac identycznie.
## `productsApi.js`
Plik:
```txt
frontend/src/services/productsApi.js
```
Eksportuje:
```js
fetchProducts(params)
```
Odpowiedzialnosc:
- pobranie listy produktow,
- obsluga filtrow,
- obsluga `limit` i `offset`,
- zwrocenie danych w formacie oczekiwanym przez `productsStore`.
Zwraca:
```js
{
items: [],
pageInfo: {
limit: 30,
offset: 0,
hasMore: true,
total: 184,
},
}
```
W trybie mock:
- czyta `frontend/src/mocks/products.json`,
- filtruje po `search`, `finish`, `year`, `productionList`,
- ucina wynik wedlug `limit` i `offset`,
- zwraca `items` i `pageInfo`.
W trybie prawdziwego API:
```http
GET /mayo-api/products
```
z parametrami:
```js
{
limit,
offset,
search,
model,
client,
finish,
productionList,
year,
}
```
## `productTimelineApi.js`
Plik:
```txt
frontend/src/services/productTimelineApi.js
```
Eksportuje:
```js
fetchProductTimeline(productId)
createProductTimelineEvent(productId, payload)
```
### `fetchProductTimeline`
Pobiera pelny timeline produktu.
W trybie mock:
- czyta `frontend/src/mocks/timelines.json`,
- zwraca timeline dla `productId`,
- jesli brak danych, zwraca pusty timeline.
W trybie prawdziwego API:
```http
GET /mayo-api/products/:id/timeline
```
### `createProductTimelineEvent`
Dodaje event do timeline.
W trybie mock:
- tworzy sztuczny event,
- dodaje go do pamieci mockow,
- przelicza `timelinePreview`,
- zwraca nowy event i nowy preview.
W trybie prawdziwego API:
```http
POST /mayo-api/products/:id/timeline/events
```
Zwracany format:
```js
{
event: {},
timelinePreview: {
body: [],
neck: [],
},
}
```
To pozwala store od razu zaktualizowac:
- pelny timeline,
- preview na glownej karcie produktu.
## `productSpecificationApi.js`
Plik:
```txt
frontend/src/services/productSpecificationApi.js
```
Eksportuje:
```js
fetchProductSpecification(productId)
refreshProductSpecification(productId)
```
### `fetchProductSpecification`
Pobiera specyfikacje produktu.
W trybie mock:
- czyta `frontend/src/mocks/specifications.json`.
W trybie prawdziwego API:
```http
GET /mayo-api/products/:id/specification
```
### `refreshProductSpecification`
Odswieza specyfikacje produktu.
W trybie mock:
- zwraca dane z mocka,
- aktualizuje `lastFetchedAt` na aktualny czas.
W trybie prawdziwego API:
```http
POST /mayo-api/products/:id/specification/refresh
```
### Normalizacja odpowiedzi
Ten plik zawiera funkcje:
```js
normalizeSpecificationResponse(data)
```
Jej zadaniem jest zamiana odpowiedzi backendu na prosty format dla store:
```js
{
productId,
orderId,
sourceUrl,
lastFetchedAt,
sections,
diff,
}
```
Service akceptuje dwa ksztalty:
```js
data.specification.sections
```
albo:
```js
data.sections
```
Dzieki temu store nie musi znac szczegolow odpowiedzi HTTP.
## `dictionariesApi.js`
Plik:
```txt
frontend/src/services/dictionariesApi.js
```
Eksportuje:
```js
fetchDictionaries()
```
Odpowiedzialnosc:
- pobranie slownikow do filtrow i formularzy,
- normalizacja pustych pol do pustych tablic.
W trybie mock:
- czyta `frontend/src/mocks/dictionaries.json`.
W trybie prawdziwego API:
```http
GET /mayo-api/dictionaries
```
Zwraca:
```js
{
models: [],
clients: [],
finishes: [],
productionLists: [],
operations: [],
colors: [],
}
```
## Jak dodac nowy endpoint
Przyklad: chcesz dodac API do zdjec produktu.
Nie dodawaj Axiosa bezposrednio w komponencie ani w store.
Zrob nowy service:
```txt
frontend/src/services/productPhotosApi.js
```
W nim:
```js
import { api } from 'src/boot/axios'
import { USE_MOCK_API, waitForMockApi } from 'src/services/apiMode'
export async function fetchProductPhotos(productId) {
if (USE_MOCK_API) {
await waitForMockApi()
return []
}
const response = await api.get(`/mayo-api/products/${productId}/photos`)
return response.data
}
```
Dopiero potem store importuje:
```js
import { fetchProductPhotos } from 'src/services/productPhotosApi'
```
## Zasady pracy z mockami
Mocki powinny miec taki sam ksztalt jak prawdziwe API.
To jest najwazniejsza zasada.
Jesli prawdziwy endpoint ma zwrocic:
```js
{
items: [],
pageInfo: {}
}
```
to mock tez powinien zwrocic:
```js
{
items: [],
pageInfo: {}
}
```
Nie nalezy robic osobnych struktur danych tylko dla widoku, jesli nie beda zgodne z przyszlym API.
## Co zostalo juz zrobione
Utworzono:
```txt
frontend/src/services/apiMode.js
frontend/src/services/productsApi.js
frontend/src/services/productTimelineApi.js
frontend/src/services/productSpecificationApi.js
frontend/src/services/dictionariesApi.js
```
Utworzono mocki:
```txt
frontend/src/mocks/products.json
frontend/src/mocks/timelines.json
frontend/src/mocks/specifications.json
frontend/src/mocks/dictionaries.json
```
Zaktualizowano store tak, aby uzywaly services zamiast Axiosa bezposrednio.
Zaktualizowano widok glownej strony tak, aby pobieral dane ze store zamiast trzymac lokalne mocki w komponencie.
## Najwazniejsza regula
Komponenty nie powinny importowac:
```js
api
```
ani:
```js
src/mocks/*.json
```
Komponent powinien uzywac store albo dostawac dane przez props.
Store powinien uzywac services.
Services powinny uzywac Axiosa albo mockow.

437
notes/arch_struct.md Normal file
View File

@@ -0,0 +1,437 @@
# Architektura Danych I Endpointow
Data: 2026-04-29
## Cel dokumentu
Ten dokument podsumowuje ustalenia dotyczace architektury danych pomiedzy:
- frontendem Quasar/Vue,
- Directusem jako glownym backendem dla frontendu,
- FastAPI jako integracja ze starym systemem Mayo.
Najwazniejszy problem, ktory rozwiazywalismy:
> Ile danych frontend powinien pobierac i trzymac w pamieci oraz jaki ksztalt powinny miec endpointy Directusa, zeby aplikacja byla szybka, czytelna i latwa do dalszej rozbudowy.
## Glowne zalozenie
Frontend nie powinien pobierac pelnych danych kazdego produktu na glowna liste.
Glowna lista produktow powinna dostawac lekki obiekt przygotowany specjalnie pod widok listy. Pelna specyfikacja i pelny timeline powinny byc pobierane dopiero wtedy, gdy uzytkownik ich potrzebuje.
To jest dobre rozwiazanie, bo:
- lista produktow bedzie szybciej sie ladowac,
- virtual scroll i lazy loading beda prostsze,
- frontend nie bedzie trzymal w pamieci danych, ktorych uzytkownik moze nigdy nie otworzyc,
- backend moze przygotowac dane dokladnie pod ekran,
- komponenty Vue nie musza znac struktury tabel w Directusie.
## Trzy rozne reprezentacje produktu
Ten sam produkt moze miec kilka reprezentacji w aplikacji. To nie jest blad ani niepotrzebna duplikacja. To jest normalny podzial danych pod konkretne widoki.
### `ProductListItem`
Lekki obiekt do glownej listy produktow.
Zawiera tylko dane potrzebne do pokazania karty produktu, filtrowania i szybkiej pracy listy:
```js
{
id: 101,
orderId: '0143/2025/1',
orderNumber: '0143',
orderYear: 2025,
orderIndex: 1,
model: 'Regius Core 6',
client: 'HIENDGUITAR.COM / INDONESIA',
finish: 'S+M',
productionLists: ['CZE-00'],
timelinePreview: {
body: [
{
id: 9001,
code: 'B',
label: 'Bejca',
date: '2026-04-20',
status: 'done',
},
],
neck: [],
},
}
```
To jest format, z ktorego korzysta karta produktu na glownej stronie.
### `ProductTimeline`
Pelny timeline jednego produktu.
Jest pobierany osobno, np. do prawego drawera albo widoku szczegolowego:
```js
{
productId: 101,
events: [
{
id: 9001,
productId: 101,
partId: 501,
partType: 'BODY',
type: 'operation',
operationId: 1,
operationCode: 'B',
operationName: 'Bejca',
date: '2026-04-20',
note: null,
photosCount: 0,
},
{
id: 9006,
productId: 101,
partId: 501,
partType: 'BODY',
type: 'note',
operationId: null,
operationCode: null,
operationName: null,
date: '2026-04-23',
note: 'Do sprawdzenia rownomiernosc koloru.',
photosCount: 2,
},
],
timelinePreview: {
body: [],
neck: [],
},
}
```
Pelny timeline zawiera operacje, notatki i pozniej moze zawierac informacje o zdjeciach.
### `ProductSpecification`
Pelna specyfikacja produktu ze starego systemu Mayo.
Nie powinna byc czescia `ProductListItem`. Jest pobierana dopiero po otwarciu panelu specyfikacji:
```js
{
productId: 101,
orderId: '0143/2025/1',
sourceUrl: 'http://10.8.0.6/mayo2/index.php?...',
lastFetchedAt: '2026-04-22T10:30:00Z',
sections: [
{
key: 'szyjka',
label: 'Szyjka',
fields: [
{
key: 'radius',
label: 'Radius',
values: ['GITARA SETIUS/REGIUS/CUSTOM/ 16'],
},
],
},
],
diff: [],
}
```
## Dlaczego timeline jest w dwoch miejscach
Na glownej liscie produktow potrzebny jest tylko skrot timeline:
```js
timelinePreview
```
W prawym drawerze albo widoku szczegolowym potrzebna jest pelna historia:
```js
events
```
Dlatego mamy dwa poziomy danych:
```txt
ProductListItem.timelinePreview
szybki skrot na karte produktu
ProductTimeline.events
pelny timeline do szczegolow
```
To jest dobre rozwiazanie, bo na liscie moze byc duzo produktow. Gdyby kazdy produkt mial pelny timeline, frontend pobieralby i przechowywal duzo danych, ktore czesto nie beda uzyte.
## Czy produkt na liscie powinien miec pelna specyfikacje
Nie.
Produkt na liscie nie powinien miec pelnej specyfikacji. Powinien miec tylko informacje potrzebne do pokazania karty:
- numer zamowienia,
- model,
- klient,
- finish,
- listy produkcyjne,
- skrot timeline.
Pelna specyfikacja powinna byc pobierana przez osobny endpoint po kliknieciu produktu albo otwarciu drawera.
## Rola backendu Directus
Directus powinien byc glownym API dla frontendu.
Frontend nie powinien skladac produktu z wielu tabel Directusa. Backend powinien przygotowac gotowy ksztalt odpowiedzi pod konkretny ekran.
To oznacza, ze zamiast zmuszac frontend do pobierania:
```txt
products
orders
clients
models
parts
events
lists
```
lepiej utworzyc custom endpoint:
```http
GET /mayo-api/products
```
ktory zwroci gotowe `ProductListItem`.
To jest dobre rozwiazanie, bo:
- frontend jest prostszy,
- mniej logiki laczenia danych jest w Vue,
- backend moze zoptymalizowac zapytania SQL,
- jeden endpoint odpowiada jednemu widokowi aplikacji,
- latwiej utrzymac stabilny kontrakt API.
## Rola FastAPI
FastAPI nie powinien byc glownym API dla frontendu.
Jego rola to integracja ze starym systemem Mayo:
- pobranie specyfikacji po numerze zamowienia,
- parsowanie strony starego systemu,
- zwrocenie danych Directusowi albo procesowi importu.
Docelowy przeplyw:
```txt
frontend -> Directus -> FastAPI -> stary system Mayo
```
Frontend powinien jak najczesciej rozmawiac tylko z Directusem.
## Planowane endpointy
### Lista produktow
```http
GET /mayo-api/products
```
Parametry:
```http
?limit=30&offset=0&search=regius&finish=GLOSS&year=2025&productionList=CZE-00
```
Cel:
- pobieranie danych do glownej listy,
- obsluga virtual scroll i lazy loading,
- filtrowanie,
- zwrocenie `timelinePreview`.
Przykladowa odpowiedz:
```json
{
"items": [],
"pageInfo": {
"limit": 30,
"offset": 0,
"hasMore": true,
"total": 184
}
}
```
### Pelny timeline produktu
```http
GET /mayo-api/products/:id/timeline
```
Cel:
- pobranie pelnej historii produkcji jednego produktu,
- uzycie w prawym drawerze albo widoku szczegolowym.
### Dodanie eventu do timeline
```http
POST /mayo-api/products/:id/timeline/events
```
Cel:
- dodanie operacji, notatki albo innego wpisu produkcyjnego.
Backend powinien zwrocic:
- utworzony event,
- opcjonalnie nowy `timelinePreview`.
Dzieki temu frontend moze od razu:
- dopisac event do pelnego timeline,
- zaktualizowac skrot na karcie produktu.
### Pelna specyfikacja produktu
```http
GET /mayo-api/products/:id/specification
```
Cel:
- pobranie pelnej specyfikacji jednego produktu,
- uzycie w panelu specyfikacji.
### Odswiezenie specyfikacji
```http
POST /mayo-api/products/:id/specification/refresh
```
Cel:
- Directus prosi FastAPI o pobranie aktualnych danych ze starego systemu,
- backend liczy hash/diff,
- zapisuje nowa wersje tylko jesli dane sie zmienily,
- zwraca aktualny stan specyfikacji.
### Slowniki
```http
GET /mayo-api/dictionaries
```
Cel:
- pobranie danych do filtrow i formularzy.
Przyklady:
- modele,
- klienci,
- finisze,
- listy produkcyjne,
- operacje,
- kolory.
## Virtual scroll i lazy loading
Glowna lista powinna dzialac na porcjach danych.
Przyklad:
```txt
limit = 30
offset = 0
```
Nastepne pobranie:
```txt
limit = 30
offset = 30
```
Frontend nie powinien pobierac calej bazy tylko dlatego, ze lista nie ma paginacji widocznej dla uzytkownika.
Lista moze wygladac jak jedna ciagla lista, ale technicznie powinna pobierac dane porcjami.
## Nazewnictwo `productId` i `orderId`
Wazne rozroznienie:
```js
productId
```
To wewnetrzne ID produktu w Directusie.
```js
orderId
```
To numer starego systemu Mayo, np.:
```txt
0143/2025/1
```
Nie nalezy uzywac `orderId` jako `productId`.
## Podzial odpowiedzialnosci
Docelowy przeplyw:
```txt
komponent Vue
-> store Pinia
-> service API
-> Axios albo mock JSON
-> Directus
```
Komponent:
- renderuje UI,
- dostaje dane przez props,
- emituje zdarzenia.
Store:
- trzyma stan,
- trzyma loading/error,
- zarzadza cache,
- decyduje kiedy pobrac dane.
Service API:
- zna endpointy,
- zna Axios,
- normalizuje odpowiedzi backendu.
Backend:
- laczy dane z tabel,
- przygotowuje odpowiedz pod frontend,
- pilnuje spojnosci danych.
## Dlaczego to jest dobre rozwiazanie
Ten podzial jest dobry, bo utrzymuje osobne odpowiedzialnosci:
- komponenty nie wiedza nic o Directusie,
- store nie musza znac szczegolow Axiosa,
- services nie przechowuja stanu,
- backend moze zmieniac strukture bazy bez przepisywania calego frontendu,
- mock API pozwala pracowac nad wygladem bez gotowego backendu.
To jest praktyczny kompromis dla MVP: nie jest przesadnie skomplikowany, ale od razu porzadkuje najwazniejsze granice aplikacji.

1071
notes/stores.md Normal file

File diff suppressed because it is too large Load Diff