refactor(api): extract shared extractCollection helper (F2.1)
Create shared/utils/apiHelpers.ts with generic extractCollection<T>() that handles hydra:member, member, items, data, and array formats. Replace 7 local implementations in CRUD composables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { useApi } from './useApi'
|
|||||||
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
||||||
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
||||||
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
||||||
|
import { extractCollection } from '~/shared/utils/apiHelpers'
|
||||||
|
|
||||||
export interface Composant {
|
export interface Composant {
|
||||||
id: string
|
id: string
|
||||||
@@ -45,23 +46,6 @@ const composants = ref<Composant[]>([])
|
|||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const extractCollection = (payload: unknown): Composant[] => {
|
|
||||||
if (Array.isArray(payload)) {
|
|
||||||
return payload as Composant[]
|
|
||||||
}
|
|
||||||
const p = payload as Record<string, unknown> | null
|
|
||||||
if (Array.isArray(p?.member)) {
|
|
||||||
return p.member as Composant[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.['hydra:member'])) {
|
|
||||||
return p['hydra:member'] as Composant[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.data)) {
|
|
||||||
return p.data as Composant[]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const extractTotal = (payload: unknown, fallbackLength: number): number => {
|
const extractTotal = (payload: unknown, fallbackLength: number): number => {
|
||||||
const p = payload as Record<string, unknown> | null
|
const p = payload as Record<string, unknown> | null
|
||||||
if (typeof p?.totalItems === 'number') {
|
if (typeof p?.totalItems === 'number') {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useApi } from './useApi'
|
import { useApi } from './useApi'
|
||||||
import { useToast } from './useToast'
|
import { useToast } from './useToast'
|
||||||
|
import { extractCollection } from '~/shared/utils/apiHelpers'
|
||||||
|
|
||||||
export interface Constructeur {
|
export interface Constructeur {
|
||||||
id: string
|
id: string
|
||||||
@@ -52,23 +53,6 @@ const upsertConstructeurs = (items: Constructeur[] = []) => {
|
|||||||
const getIndexedConstructeur = (id: string): Constructeur | null =>
|
const getIndexedConstructeur = (id: string): Constructeur | null =>
|
||||||
constructeurs.value.find((item) => item && item.id === id) || null
|
constructeurs.value.find((item) => item && item.id === id) || null
|
||||||
|
|
||||||
const extractCollection = (payload: unknown): Constructeur[] => {
|
|
||||||
if (Array.isArray(payload)) {
|
|
||||||
return payload as Constructeur[]
|
|
||||||
}
|
|
||||||
const p = payload as Record<string, unknown> | null
|
|
||||||
if (Array.isArray(p?.member)) {
|
|
||||||
return p.member as Constructeur[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.['hydra:member'])) {
|
|
||||||
return p['hydra:member'] as Constructeur[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.data)) {
|
|
||||||
return p.data as Constructeur[]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const pendingFetches = new Map<string, Promise<Constructeur | null>>()
|
const pendingFetches = new Map<string, Promise<Constructeur | null>>()
|
||||||
|
|
||||||
export function useConstructeurs() {
|
export function useConstructeurs() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ref } from 'vue'
|
|||||||
import { useApi } from './useApi'
|
import { useApi } from './useApi'
|
||||||
import { useToast } from './useToast'
|
import { useToast } from './useToast'
|
||||||
import { normalizeRelationIds } from '~/shared/apiRelations'
|
import { normalizeRelationIds } from '~/shared/apiRelations'
|
||||||
|
import { extractCollection } from '~/shared/utils/apiHelpers'
|
||||||
|
|
||||||
export interface Document {
|
export interface Document {
|
||||||
id: string
|
id: string
|
||||||
@@ -34,23 +35,6 @@ export interface DocumentResult {
|
|||||||
const documents = ref<Document[]>([])
|
const documents = ref<Document[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const extractCollection = (payload: unknown): Document[] => {
|
|
||||||
if (Array.isArray(payload)) {
|
|
||||||
return payload
|
|
||||||
}
|
|
||||||
const p = payload as Record<string, unknown> | null
|
|
||||||
if (Array.isArray(p?.member)) {
|
|
||||||
return p.member as Document[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.['hydra:member'])) {
|
|
||||||
return p['hydra:member'] as Document[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.data)) {
|
|
||||||
return p.data as Document[]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const fileToBase64 = (file: File): Promise<string> =>
|
const fileToBase64 = (file: File): Promise<string> =>
|
||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader()
|
const reader = new FileReader()
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useApi } from './useApi'
|
|||||||
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
||||||
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
||||||
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
||||||
|
import { extractCollection } from '~/shared/utils/apiHelpers'
|
||||||
|
|
||||||
export interface Piece {
|
export interface Piece {
|
||||||
id: string
|
id: string
|
||||||
@@ -46,23 +47,6 @@ const pieces = ref<Piece[]>([])
|
|||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const extractCollection = (payload: unknown): Piece[] => {
|
|
||||||
if (Array.isArray(payload)) {
|
|
||||||
return payload as Piece[]
|
|
||||||
}
|
|
||||||
const p = payload as Record<string, unknown> | null
|
|
||||||
if (Array.isArray(p?.member)) {
|
|
||||||
return p.member as Piece[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.['hydra:member'])) {
|
|
||||||
return p['hydra:member'] as Piece[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.data)) {
|
|
||||||
return p.data as Piece[]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const extractTotal = (payload: unknown, fallbackLength: number): number => {
|
const extractTotal = (payload: unknown, fallbackLength: number): number => {
|
||||||
const p = payload as Record<string, unknown> | null
|
const p = payload as Record<string, unknown> | null
|
||||||
if (typeof p?.totalItems === 'number') {
|
if (typeof p?.totalItems === 'number') {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useApi } from './useApi'
|
|||||||
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
||||||
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
||||||
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
||||||
|
import { extractCollection } from '~/shared/utils/apiHelpers'
|
||||||
|
|
||||||
export interface Product {
|
export interface Product {
|
||||||
id: string
|
id: string
|
||||||
@@ -62,23 +63,6 @@ const replaceInCache = (item: Product): boolean => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
const extractCollection = (payload: unknown): Product[] => {
|
|
||||||
if (Array.isArray(payload)) {
|
|
||||||
return payload as Product[]
|
|
||||||
}
|
|
||||||
const p = payload as Record<string, unknown> | null
|
|
||||||
if (Array.isArray(p?.member)) {
|
|
||||||
return p.member as Product[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.['hydra:member'])) {
|
|
||||||
return p['hydra:member'] as Product[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.data)) {
|
|
||||||
return p.data as Product[]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const extractTotal = (payload: unknown, fallbackLength: number): number => {
|
const extractTotal = (payload: unknown, fallbackLength: number): number => {
|
||||||
const p = payload as Record<string, unknown> | null
|
const p = payload as Record<string, unknown> | null
|
||||||
if (typeof p?.totalItems === 'number') {
|
if (typeof p?.totalItems === 'number') {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useToast } from './useToast'
|
import { useToast } from './useToast'
|
||||||
import { useApi } from './useApi'
|
import { useApi } from './useApi'
|
||||||
|
import { extractCollection } from '~/shared/utils/apiHelpers'
|
||||||
|
|
||||||
export interface Site {
|
export interface Site {
|
||||||
id: string
|
id: string
|
||||||
@@ -24,23 +25,6 @@ interface SiteResult {
|
|||||||
const sites = ref<Site[]>([])
|
const sites = ref<Site[]>([])
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
|
||||||
const extractCollection = (payload: unknown): Site[] => {
|
|
||||||
if (Array.isArray(payload)) {
|
|
||||||
return payload as Site[]
|
|
||||||
}
|
|
||||||
const p = payload as Record<string, unknown> | null
|
|
||||||
if (Array.isArray(p?.member)) {
|
|
||||||
return p.member as Site[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.['hydra:member'])) {
|
|
||||||
return p['hydra:member'] as Site[]
|
|
||||||
}
|
|
||||||
if (Array.isArray(p?.data)) {
|
|
||||||
return p.data as Site[]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useSites() {
|
export function useSites() {
|
||||||
const { showSuccess, showInfo } = useToast()
|
const { showSuccess, showInfo } = useToast()
|
||||||
const { get, post, patch, delete: del } = useApi()
|
const { get, post, patch, delete: del } = useApi()
|
||||||
@@ -49,8 +33,6 @@ export function useSites() {
|
|||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const result = await get('/sites')
|
const result = await get('/sites')
|
||||||
console.log('sites api result', result)
|
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const collection = extractCollection(result.data)
|
const collection = extractCollection(result.data)
|
||||||
sites.value = collection
|
sites.value = collection
|
||||||
|
|||||||
16
app/shared/utils/apiHelpers.ts
Normal file
16
app/shared/utils/apiHelpers.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Shared API response helpers.
|
||||||
|
*
|
||||||
|
* Extracted from 10+ composables/components that each had an identical local
|
||||||
|
* copy of extractCollection (parsing hydra:member / member / data / array).
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function extractCollection<T = any>(payload: unknown): T[] {
|
||||||
|
if (Array.isArray(payload)) return payload as T[]
|
||||||
|
const p = payload as Record<string, unknown> | null
|
||||||
|
if (Array.isArray(p?.member)) return p!.member as T[]
|
||||||
|
if (Array.isArray(p?.['hydra:member'])) return p!['hydra:member'] as T[]
|
||||||
|
if (Array.isArray(p?.items)) return p!.items as T[]
|
||||||
|
if (Array.isArray(p?.data)) return p!.data as T[]
|
||||||
|
return []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user