feat: sélecteur de famille (onglet Sheets) en sync et création

- GET /api/sheets/tabs : liste les onglets Google Sheets disponibles
- readSheetProducts(tab?) et fetchPendingProducts(set, tab?) acceptent un filtre
- /api/sync/preview?tab=xxx et /api/creation/preview?tab=xxx filtrent sur un onglet
- Composant FamilySelector : grille de boutons chargeant les onglets dynamiquement
- Page Sync : étape 0 de sélection famille avant l'analyse
- Page Création : étape famille avant analyse, badge de famille sélectionnée

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 23:31:19 +02:00
parent 1b07359415
commit 23aa78d3da
8 changed files with 180 additions and 30 deletions
+14 -8
View File
@@ -82,19 +82,25 @@ export function parseTabRows(tab: string, headers: string[], rows: string[][], d
export async function fetchPendingProducts(
alreadyCreatedRows: Set<string>,
tabFilter?: string,
): Promise<PendingProduct[]> {
const apiKey = process.env.GOOGLE_API_KEY
const sheetId = process.env.GOOGLE_SHEETS_ID
if (!apiKey || !sheetId) throw new Error('GOOGLE_API_KEY et GOOGLE_SHEETS_ID requis')
const metaRes = await fetch(
`https://sheets.googleapis.com/v4/spreadsheets/${sheetId}?key=${apiKey}&fields=sheets.properties.title`,
)
if (!metaRes.ok) throw new Error(`Sheets metadata error: ${metaRes.status}`)
const meta = await metaRes.json()
const tabs: string[] = (meta.sheets as Array<{ properties: { title: string } }>).map(
s => s.properties.title,
)
let tabs: string[]
if (tabFilter) {
tabs = [tabFilter]
} else {
const metaRes = await fetch(
`https://sheets.googleapis.com/v4/spreadsheets/${sheetId}?key=${apiKey}&fields=sheets.properties.title`,
)
if (!metaRes.ok) throw new Error(`Sheets metadata error: ${metaRes.status}`)
const meta = await metaRes.json()
tabs = (meta.sheets as Array<{ properties: { title: string } }>).map(
s => s.properties.title,
)
}
const all: PendingProduct[] = []
for (const tab of tabs) {
+3 -3
View File
@@ -52,16 +52,16 @@ export function parseSheetRows(rows: string[][]): SyncProduct[] {
* Sinon, lit tous les onglets du fichier et combine les produits.
* Le fichier Sheets doit être partagé en lecture ("Toute personne avec le lien").
*/
export async function readSheetProducts(): Promise<SyncProduct[]> {
export async function readSheetProducts(tab?: string): Promise<SyncProduct[]> {
const apiKey = process.env.GOOGLE_API_KEY
const sheetId = process.env.GOOGLE_SHEETS_ID
const singleTab = process.env.GOOGLE_SHEETS_TAB
const envTab = process.env.GOOGLE_SHEETS_TAB
if (!apiKey || !sheetId) {
throw new Error('Variables GOOGLE_API_KEY et GOOGLE_SHEETS_ID requises')
}
const sheetNames = singleTab ? [singleTab] : await listSheetTabs(sheetId, apiKey)
const sheetNames = tab ? [tab] : envTab ? [envTab] : await listSheetTabs(sheetId, apiKey)
const results = await Promise.all(
sheetNames.map((name) => readOneTab(sheetId, apiKey, name)),