@burdenoff/microfe-coolandlovely 2026.706.2 → 2026.706.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/coolandlovely/CoolAndLovelyRoot.js +62 -46
- package/dist/coolandlovely/CoolAndLovelyRoot.js.map +1 -1
- package/dist/coolandlovely/components/AddToLookbookButton.js +22 -22
- package/dist/coolandlovely/components/AddToLookbookButton.js.map +1 -1
- package/dist/coolandlovely/components/BrandCover.js +39 -0
- package/dist/coolandlovely/components/BrandCover.js.map +1 -0
- package/dist/coolandlovely/components/BrandCreatorAvatars.js +57 -0
- package/dist/coolandlovely/components/BrandCreatorAvatars.js.map +1 -0
- package/dist/coolandlovely/components/ColourSwatchRow.js +86 -0
- package/dist/coolandlovely/components/ColourSwatchRow.js.map +1 -0
- package/dist/coolandlovely/components/Comments.js +4 -4
- package/dist/coolandlovely/components/Comments.js.map +1 -1
- package/dist/coolandlovely/components/FileImage.js.map +1 -1
- package/dist/coolandlovely/components/ImageUploadField.js +139 -57
- package/dist/coolandlovely/components/ImageUploadField.js.map +1 -1
- package/dist/coolandlovely/components/LookCard.js +77 -0
- package/dist/coolandlovely/components/LookCard.js.map +1 -0
- package/dist/coolandlovely/components/MetricsCard.js +1 -1
- package/dist/coolandlovely/components/MetricsCard.js.map +1 -1
- package/dist/coolandlovely/components/ShopItemCard.js +45 -0
- package/dist/coolandlovely/components/ShopItemCard.js.map +1 -0
- package/dist/coolandlovely/components/StepTracker.js +35 -0
- package/dist/coolandlovely/components/StepTracker.js.map +1 -0
- package/dist/coolandlovely/components/Thumbnail.js +15 -11
- package/dist/coolandlovely/components/Thumbnail.js.map +1 -1
- package/dist/coolandlovely/components/VisibilityChip.js +32 -0
- package/dist/coolandlovely/components/VisibilityChip.js.map +1 -0
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +94 -69
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/hooks/useFiles.js +49 -32
- package/dist/coolandlovely/hooks/useFiles.js.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js +112 -133
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js +253 -242
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +116 -112
- package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +13 -13
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js +117 -95
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationFormPage.js +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js +50 -43
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js +131 -108
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionFormPage.js +1 -1
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js +59 -56
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js +1 -1
- package/dist/coolandlovely/pages/discover/DiscoverPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js +175 -147
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemFormPage.js +1 -1
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js +71 -62
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js +149 -127
- package/dist/coolandlovely/pages/lookbooks/LookbookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js +69 -53
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.js +135 -113
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookFormPage.js +122 -110
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.js +60 -97
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js +372 -210
- package/dist/coolandlovely/pages/profile/StyleProfilePage.js.map +1 -1
- package/dist/coolandlovely/pages/trends/TrendsPage.js +1 -1
- package/dist/coolandlovely/pages/trends/TrendsPage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js +130 -94
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js +75 -76
- package/dist/coolandlovely/pages/wardrobe/WardrobeListPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCoolAndLovelyApi.js","names":[],"sources":["../../../src/coolandlovely/hooks/useCoolAndLovelyApi.ts"],"sourcesContent":["/**\n * CoolAndLovely API hooks\n * @module coolandlovely/hooks\n *\n * Thin, strictly-typed React Query layer over the federated\n * `wspace-coolandlovely-svc` GraphQL contract. Every query/mutation here maps\n * 1:1 to a real `coo*` root field — no mocks. Requests go through the workspace\n * gateway via `graphqlFetch({ gateway: 'workspace', workspaceToken: true })`.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n// Imported from the `/fetch` subpath (not the barrel) so it resolves against the\n// MFE's pinned fe-libs, which exposes the helper in fetch but not yet in the\n// graphql barrel index.\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\nimport { useCoolAndLovely } from '../context';\nimport type {\n CooAddLookbookEntryInput,\n CooBrand,\n CooBrandConnection,\n CooBrandStatus,\n CooCollaboration,\n CooCollaborationStatus,\n CooCollection,\n CooCollectionStatus,\n CooCreateBrandInput,\n CooCreateCollaborationInput,\n CooCreateCollectionInput,\n CooCreateLookInput,\n CooCreateLookbookInput,\n CooCreateStyleItemInput,\n CooCreateWardrobeItemInput,\n CooLook,\n CooLookConnection,\n CooLookOccasion,\n CooLookbook,\n CooStyleCategory,\n CooStyleItem,\n CooStyleItemConnection,\n CooStyleItemStatus,\n CooStyleProfile,\n CooUpdateBrandInput,\n CooUpdateCollectionInput,\n CooUpdateLookInput,\n CooUpdateStyleItemInput,\n CooUpdateWardrobeItemInput,\n CooUpsertStyleProfileInput,\n CooWardrobeItem,\n CooWardrobeSource,\n PaginationInput,\n} from '../types';\n\n// ---------------------------------------------------------------------------\n// Query key root\n// ---------------------------------------------------------------------------\n\nconst QK = 'coolandlovely';\n\n// ---------------------------------------------------------------------------\n// Low-level fetch wrapper\n// ---------------------------------------------------------------------------\n\nconst coolQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n // fe-libs graphqlFetch caches every *query* response for ~15s in a global\n // response cache. After a successful mutation we drop that cache so the\n // React Query `invalidateQueries` refetch reads fresh data instead of the\n // stale pre-mutation response (otherwise a like/publish/transition/remove\n // shows no change for up to 15s).\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlFetchResponseCache();\n }\n return response.data;\n};\n\n// ---------------------------------------------------------------------------\n// Reusable GraphQL field selections\n// ---------------------------------------------------------------------------\n\nconst BRAND_FIELDS = `\n id workspaceId name handle tagline bio aesthetics tags websiteUrl\n isFeatured status logo { id } cover { id } createdAt updatedAt\n`;\n\nconst COLLECTION_FIELDS = `\n id workspaceId brandId name description season status tags\n cover { id } publishAt endAt createdAt updatedAt\n`;\n\nconst STYLE_ITEM_FIELDS = `\n id workspaceId brandId collectionId name description category status\n sku price currency colours sizes materials tags image { id }\n createdAt updatedAt\n`;\n\nconst LOOK_FIELDS = `\n id workspaceId authorId title caption occasion visibility isFeatured\n likeCount saveCount collaborationId image { id } tags publishedAt\n createdAt updatedAt\n`;\n\nconst COLLABORATION_FIELDS = `\n id workspaceId brandId creatorId collectionId title brief role status\n compensation currency tags startDate endDate publishedAt createdAt updatedAt\n`;\n\nconst WARDROBE_FIELDS = `\n id workspaceId profileId name category source brandName colour size notes\n styleItemId timesWorn lastWornAt tags image { id } createdAt updatedAt\n`;\n\nconst PROFILE_FIELDS = `\n id workspaceId ownerId displayName bio visibility stylePreferences\n palette favouriteBrands sizes createdAt updatedAt\n isFollowedByMe followerCount followingCount\n`;\n\nconst LOOKBOOK_FIELDS = `\n id workspaceId ownerId profileId name description visibility tags\n cover { id } createdAt updatedAt\n`;\n\n// ===========================================================================\n// Brands\n// ===========================================================================\n\nexport interface ListBrandsArgs {\n status?: CooBrandStatus;\n featured?: boolean;\n pagination?: PaginationInput;\n}\n\nexport const useBrands = (args: ListBrandsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'brands', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListBrands: CooBrandConnection }>(\n `query CooListBrands($status: CooBrandStatus, $featured: Boolean, $pagination: PaginationInput) {\n cooListBrands(status: $status, featured: $featured, pagination: $pagination) {\n items { ${BRAND_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: args.status,\n featured: args.featured,\n pagination: args.pagination ?? { limit: 50, offset: 0 },\n },\n apiGatewayUrl\n );\n return data.cooListBrands;\n },\n });\n};\n\nexport const useBrand = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'brand', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooBrand: CooBrand | null }>(\n `query CooBrand($id: ID!) {\n cooBrand(id: $id) {\n ${BRAND_FIELDS}\n collections { ${COLLECTION_FIELDS} }\n items { ${STYLE_ITEM_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooBrand;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateBrandInput) => {\n const data = await coolQuery<{ cooCreateBrand: CooBrand }>(\n `mutation CooCreateBrand($input: CooCreateBrandInput!) {\n cooCreateBrand(input: $input) { ${BRAND_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateBrand;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n },\n });\n};\n\nexport const useUpdateBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateBrandInput }) => {\n const data = await coolQuery<{ cooUpdateBrand: CooBrand }>(\n `mutation CooUpdateBrand($id: ID!, $input: CooUpdateBrandInput!) {\n cooUpdateBrand(id: $id, input: $input) { ${BRAND_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateBrand;\n },\n onSuccess: (brand) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'brand', brand.id] });\n },\n });\n};\n\nexport const useTransitionBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: CooBrandStatus }) => {\n const data = await coolQuery<{ cooTransitionBrand: CooBrand }>(\n `mutation CooTransitionBrand($id: ID!, $status: CooBrandStatus!) {\n cooTransitionBrand(id: $id, status: $status) { ${BRAND_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return data.cooTransitionBrand;\n },\n onSuccess: (brand) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'brand', brand.id] });\n },\n });\n};\n\nexport const useArchiveBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooArchiveBrand: boolean }>(\n `mutation CooArchiveBrand($id: ID!) { cooArchiveBrand(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooArchiveBrand;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n },\n });\n};\n\n// ===========================================================================\n// Collections\n// ===========================================================================\n\nexport interface ListCollectionsArgs {\n brandId?: string;\n status?: CooCollectionStatus;\n}\n\nexport const useCollections = (args: ListCollectionsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collections', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListCollections: CooCollection[] }>(\n `query CooListCollections($brandId: ID, $status: CooCollectionStatus) {\n cooListCollections(brandId: $brandId, status: $status) {\n ${COLLECTION_FIELDS}\n brand { id name handle }\n }\n }`,\n { brandId: args.brandId, status: args.status },\n apiGatewayUrl\n );\n return data.cooListCollections;\n },\n });\n};\n\nexport const useCollection = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collection', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooCollection: CooCollection | null }>(\n `query CooCollection($id: ID!) {\n cooCollection(id: $id) {\n ${COLLECTION_FIELDS}\n brand { id name handle }\n items { ${STYLE_ITEM_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooCollection;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateCollection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateCollectionInput) => {\n const data = await coolQuery<{ cooCreateCollection: CooCollection }>(\n `mutation CooCreateCollection($input: CooCreateCollectionInput!) {\n cooCreateCollection(input: $input) { ${COLLECTION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateCollection;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collections'] });\n },\n });\n};\n\nexport const useUpdateCollection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateCollectionInput }) => {\n const data = await coolQuery<{ cooUpdateCollection: CooCollection }>(\n `mutation CooUpdateCollection($id: ID!, $input: CooUpdateCollectionInput!) {\n cooUpdateCollection(id: $id, input: $input) { ${COLLECTION_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateCollection;\n },\n onSuccess: (collection) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collections'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'collection', collection.id] });\n },\n });\n};\n\nexport const usePublishCollection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooPublishCollection: CooCollection }>(\n `mutation CooPublishCollection($id: ID!) {\n cooPublishCollection(id: $id) { ${COLLECTION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooPublishCollection;\n },\n onSuccess: (collection) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collections'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'collection', collection.id] });\n },\n });\n};\n\n// ===========================================================================\n// Style Items\n// ===========================================================================\n\nexport interface ListStyleItemsArgs {\n brandId?: string;\n collectionId?: string;\n category?: CooStyleCategory;\n status?: CooStyleItemStatus;\n pagination?: PaginationInput;\n}\n\nexport const useStyleItems = (args: ListStyleItemsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'style-items', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListStyleItems: CooStyleItemConnection }>(\n `query CooListStyleItems(\n $brandId: ID, $collectionId: ID, $category: CooStyleCategory,\n $status: CooStyleItemStatus, $pagination: PaginationInput\n ) {\n cooListStyleItems(\n brandId: $brandId, collectionId: $collectionId, category: $category,\n status: $status, pagination: $pagination\n ) {\n items { ${STYLE_ITEM_FIELDS} brand { id name handle } }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n brandId: args.brandId,\n collectionId: args.collectionId,\n category: args.category,\n status: args.status,\n pagination: args.pagination ?? { limit: 50, offset: 0 },\n },\n apiGatewayUrl\n );\n return data.cooListStyleItems;\n },\n });\n};\n\nexport const useStyleItem = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'style-item', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooStyleItem: CooStyleItem | null }>(\n `query CooStyleItem($id: ID!) {\n cooStyleItem(id: $id) {\n ${STYLE_ITEM_FIELDS}\n brand { id name handle }\n collection { id name }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooStyleItem;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateStyleItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateStyleItemInput) => {\n const data = await coolQuery<{ cooCreateStyleItem: CooStyleItem }>(\n `mutation CooCreateStyleItem($input: CooCreateStyleItemInput!) {\n cooCreateStyleItem(input: $input) { ${STYLE_ITEM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateStyleItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-items'] });\n },\n });\n};\n\nexport const useUpdateStyleItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateStyleItemInput }) => {\n const data = await coolQuery<{ cooUpdateStyleItem: CooStyleItem }>(\n `mutation CooUpdateStyleItem($id: ID!, $input: CooUpdateStyleItemInput!) {\n cooUpdateStyleItem(id: $id, input: $input) { ${STYLE_ITEM_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateStyleItem;\n },\n onSuccess: (item) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-items'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-item', item.id] });\n },\n });\n};\n\nexport const useArchiveStyleItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooArchiveStyleItem: boolean }>(\n `mutation CooArchiveStyleItem($id: ID!) { cooArchiveStyleItem(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooArchiveStyleItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-items'] });\n },\n });\n};\n\n// ===========================================================================\n// Looks\n// ===========================================================================\n\nexport interface ListLooksArgs {\n authorId?: string;\n occasion?: CooLookOccasion;\n featured?: boolean;\n pagination?: PaginationInput;\n}\n\nexport const useLooks = (args: ListLooksArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'looks', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListLooks: CooLookConnection }>(\n `query CooListLooks(\n $authorId: ID, $occasion: CooLookOccasion, $featured: Boolean, $pagination: PaginationInput\n ) {\n cooListLooks(authorId: $authorId, occasion: $occasion, featured: $featured, pagination: $pagination) {\n items { ${LOOK_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n authorId: args.authorId,\n occasion: args.occasion,\n featured: args.featured,\n pagination: args.pagination ?? { limit: 50, offset: 0 },\n },\n apiGatewayUrl\n );\n return data.cooListLooks;\n },\n });\n};\n\nexport const useLook = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'look', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooLook: CooLook | null }>(\n `query CooLook($id: ID!) {\n cooLook(id: $id) {\n ${LOOK_FIELDS}\n items {\n id lookId styleItemId position styling\n styleItem { id name category price currency }\n }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLook;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateLookInput) => {\n const data = await coolQuery<{ cooCreateLook: CooLook }>(\n `mutation CooCreateLook($input: CooCreateLookInput!) {\n cooCreateLook(input: $input) { ${LOOK_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateLook;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n },\n });\n};\n\nexport const useUpdateLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateLookInput }) => {\n const data = await coolQuery<{ cooUpdateLook: CooLook }>(\n `mutation CooUpdateLook($id: ID!, $input: CooUpdateLookInput!) {\n cooUpdateLook(id: $id, input: $input) { ${LOOK_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateLook;\n },\n onSuccess: (look) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'look', look.id] });\n },\n });\n};\n\nexport const usePublishLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooPublishLook: CooLook }>(\n `mutation CooPublishLook($id: ID!) { cooPublishLook(id: $id) { ${LOOK_FIELDS} } }`,\n { id },\n apiGatewayUrl\n );\n return data.cooPublishLook;\n },\n onSuccess: (look) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'look', look.id] });\n },\n });\n};\n\nexport const useLikeLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooLikeLook: CooLook }>(\n `mutation CooLikeLook($id: ID!) { cooLikeLook(id: $id) { ${LOOK_FIELDS} } }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLikeLook;\n },\n onSuccess: (look) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'look', look.id] });\n },\n });\n};\n\nexport const useDeleteLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooDeleteLook: boolean }>(\n `mutation CooDeleteLook($id: ID!) { cooDeleteLook(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooDeleteLook;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n },\n });\n};\n\n// ===========================================================================\n// Collaborations\n// ===========================================================================\n\nexport interface ListCollaborationsArgs {\n brandId?: string;\n creatorId?: string;\n status?: CooCollaborationStatus;\n}\n\nexport const useCollaborations = (args: ListCollaborationsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collaborations', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListCollaborations: CooCollaboration[] }>(\n `query CooListCollaborations($brandId: ID, $creatorId: ID, $status: CooCollaborationStatus) {\n cooListCollaborations(brandId: $brandId, creatorId: $creatorId, status: $status) {\n ${COLLABORATION_FIELDS}\n brand { id name handle }\n }\n }`,\n { brandId: args.brandId, creatorId: args.creatorId, status: args.status },\n apiGatewayUrl\n );\n return data.cooListCollaborations;\n },\n });\n};\n\nexport const useCollaboration = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collaboration', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooCollaboration: CooCollaboration | null }>(\n `query CooCollaboration($id: ID!) {\n cooCollaboration(id: $id) {\n ${COLLABORATION_FIELDS}\n brand { id name handle }\n looks { ${LOOK_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooCollaboration;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateCollaboration = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateCollaborationInput) => {\n const data = await coolQuery<{ cooCreateCollaboration: CooCollaboration }>(\n `mutation CooCreateCollaboration($input: CooCreateCollaborationInput!) {\n cooCreateCollaboration(input: $input) { ${COLLABORATION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateCollaboration;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collaborations'] });\n },\n });\n};\n\nexport const useTransitionCollaboration = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: CooCollaborationStatus }) => {\n const data = await coolQuery<{ cooTransitionCollaboration: CooCollaboration }>(\n `mutation CooTransitionCollaboration($id: ID!, $status: CooCollaborationStatus!) {\n cooTransitionCollaboration(id: $id, status: $status) { ${COLLABORATION_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return data.cooTransitionCollaboration;\n },\n onSuccess: (collab) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collaborations'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'collaboration', collab.id] });\n },\n });\n};\n\n// ===========================================================================\n// Wardrobe (self-scoped)\n// ===========================================================================\n\nexport interface ListWardrobeArgs {\n category?: CooStyleCategory;\n source?: CooWardrobeSource;\n}\n\nexport const useWardrobeItems = (args: ListWardrobeArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'wardrobe', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListWardrobeItems: CooWardrobeItem[] }>(\n `query CooListWardrobeItems($category: CooStyleCategory, $source: CooWardrobeSource) {\n cooListWardrobeItems(category: $category, source: $source) { ${WARDROBE_FIELDS} }\n }`,\n { category: args.category, source: args.source },\n apiGatewayUrl\n );\n return data.cooListWardrobeItems;\n },\n });\n};\n\nexport const useCreateWardrobeItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateWardrobeItemInput) => {\n const data = await coolQuery<{ cooCreateWardrobeItem: CooWardrobeItem }>(\n `mutation CooCreateWardrobeItem($input: CooCreateWardrobeItemInput!) {\n cooCreateWardrobeItem(input: $input) { ${WARDROBE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateWardrobeItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\nexport const useUpdateWardrobeItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateWardrobeItemInput }) => {\n const data = await coolQuery<{ cooUpdateWardrobeItem: CooWardrobeItem }>(\n `mutation CooUpdateWardrobeItem($id: ID!, $input: CooUpdateWardrobeItemInput!) {\n cooUpdateWardrobeItem(id: $id, input: $input) { ${WARDROBE_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateWardrobeItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\nexport const useLogWardrobeWear = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooLogWardrobeWear: CooWardrobeItem }>(\n `mutation CooLogWardrobeWear($id: ID!) {\n cooLogWardrobeWear(id: $id) { ${WARDROBE_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLogWardrobeWear;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\nexport const useRemoveWardrobeItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooRemoveWardrobeItem: boolean }>(\n `mutation CooRemoveWardrobeItem($id: ID!) { cooRemoveWardrobeItem(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooRemoveWardrobeItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\n// ===========================================================================\n// Style Profile (self-scoped)\n// ===========================================================================\n\nexport const useMyStyleProfile = () => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'my-style-profile'],\n queryFn: async () => {\n const data = await coolQuery<{ cooMyStyleProfile: CooStyleProfile | null }>(\n `query CooMyStyleProfile { cooMyStyleProfile { ${PROFILE_FIELDS} } }`,\n {},\n apiGatewayUrl\n );\n return data.cooMyStyleProfile;\n },\n });\n};\n\nexport const useUpsertStyleProfile = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooUpsertStyleProfileInput) => {\n const data = await coolQuery<{ cooUpsertStyleProfile: CooStyleProfile }>(\n `mutation CooUpsertStyleProfile($input: CooUpsertStyleProfileInput!) {\n cooUpsertStyleProfile(input: $input) { ${PROFILE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooUpsertStyleProfile;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-style-profile'] });\n },\n });\n};\n\n// ===========================================================================\n// Lookbooks\n// ===========================================================================\n\nexport const useLookbooks = (ownerId?: string) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'lookbooks', ownerId],\n queryFn: async () => {\n const data = await coolQuery<{ cooListLookbooks: CooLookbook[] }>(\n `query CooListLookbooks($ownerId: ID) {\n cooListLookbooks(ownerId: $ownerId) { ${LOOKBOOK_FIELDS} }\n }`,\n { ownerId },\n apiGatewayUrl\n );\n return data.cooListLookbooks;\n },\n });\n};\n\nexport const useCreateLookbook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateLookbookInput) => {\n const data = await coolQuery<{ cooCreateLookbook: CooLookbook }>(\n `mutation CooCreateLookbook($input: CooCreateLookbookInput!) {\n cooCreateLookbook(input: $input) { ${LOOKBOOK_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateLookbook;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbooks'] });\n },\n });\n};\n\nexport const useAddLookbookEntry = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooAddLookbookEntryInput) => {\n const data = await coolQuery<{ cooAddLookbookEntry: { id: string; lookbookId: string } }>(\n `mutation CooAddLookbookEntry($input: CooAddLookbookEntryInput!) {\n cooAddLookbookEntry(input: $input) {\n id lookbookId lookId styleItemId note position createdAt updatedAt\n }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooAddLookbookEntry;\n },\n onSuccess: (entry) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbook'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbook', entry.lookbookId] });\n },\n });\n};\n\nexport const useRemoveLookbookEntry = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooRemoveLookbookEntry: boolean }>(\n `mutation CooRemoveLookbookEntry($id: ID!) { cooRemoveLookbookEntry(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooRemoveLookbookEntry;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbook'] });\n },\n });\n};\n\n// ===========================================================================\n// Follow graph (added this release — cooFollow / cooUnfollow / cooIsFollowing)\n// ===========================================================================\n\nconst FOLLOW_PROFILE_FIELDS = `\n id workspaceId ownerId displayName bio visibility\n stylePreferences palette favouriteBrands\n isFollowedByMe followerCount followingCount\n`;\n\n/** Whether the caller currently follows `followeeId`. */\nexport const useIsFollowing = (followeeId: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'is-following', followeeId],\n enabled: Boolean(followeeId),\n queryFn: async () => {\n const data = await coolQuery<{ cooIsFollowing: boolean }>(\n `query CooIsFollowing($followeeId: ID!) { cooIsFollowing(followeeId: $followeeId) }`,\n { followeeId },\n apiGatewayUrl\n );\n return data.cooIsFollowing;\n },\n });\n};\n\nexport const useFollow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (followeeId: string) => {\n const data = await coolQuery<{ cooFollow: boolean }>(\n `mutation CooFollow($followeeId: ID!) { cooFollow(followeeId: $followeeId) }`,\n { followeeId },\n apiGatewayUrl\n );\n return data.cooFollow;\n },\n onSuccess: (_result, followeeId) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'is-following', followeeId] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-following'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-followers'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-style-profile'] });\n },\n });\n};\n\nexport const useUnfollow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (followeeId: string) => {\n const data = await coolQuery<{ cooUnfollow: boolean }>(\n `mutation CooUnfollow($followeeId: ID!) { cooUnfollow(followeeId: $followeeId) }`,\n { followeeId },\n apiGatewayUrl\n );\n return data.cooUnfollow;\n },\n onSuccess: (_result, followeeId) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'is-following', followeeId] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-following'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-followers'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-style-profile'] });\n },\n });\n};\n\n/** Style profiles of people the caller follows. */\nexport const useMyFollowing = () => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'my-following'],\n queryFn: async () => {\n const data = await coolQuery<{ cooMyFollowing: CooStyleProfile[] }>(\n `query CooMyFollowing { cooMyFollowing { ${FOLLOW_PROFILE_FIELDS} } }`,\n {},\n apiGatewayUrl\n );\n return data.cooMyFollowing;\n },\n });\n};\n\n/** Style profiles of people following the caller. */\nexport const useMyFollowers = () => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'my-followers'],\n queryFn: async () => {\n const data = await coolQuery<{ cooMyFollowers: CooStyleProfile[] }>(\n `query CooMyFollowers { cooMyFollowers { ${FOLLOW_PROFILE_FIELDS} } }`,\n {},\n apiGatewayUrl\n );\n return data.cooMyFollowers;\n },\n });\n};\n\nexport const useLookbook = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'lookbook', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooLookbook: CooLookbook | null }>(\n `query CooLookbook($id: ID!) {\n cooLookbook(id: $id) {\n ${LOOKBOOK_FIELDS}\n entries { id lookbookId lookId styleItemId note position createdAt updatedAt }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLookbook;\n },\n enabled: Boolean(id),\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCoolAndLovelyApi.js","names":[],"sources":["../../../src/coolandlovely/hooks/useCoolAndLovelyApi.ts"],"sourcesContent":["/**\n * CoolAndLovely API hooks\n * @module coolandlovely/hooks\n *\n * Thin, strictly-typed React Query layer over the federated\n * `wspace-coolandlovely-svc` GraphQL contract. Every query/mutation here maps\n * 1:1 to a real `coo*` root field — no mocks. Requests go through the workspace\n * gateway via `graphqlFetch({ gateway: 'workspace', workspaceToken: true })`.\n */\n\nimport { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\n\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\n// Imported from the `/fetch` subpath (not the barrel) so it resolves against the\n// MFE's pinned fe-libs, which exposes the helper in fetch but not yet in the\n// graphql barrel index.\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\nimport { useCoolAndLovely } from '../context';\nimport type {\n CooAddLookbookEntryInput,\n CooBrand,\n CooBrandConnection,\n CooBrandStatus,\n CooCollaboration,\n CooCollaborationStatus,\n CooCollection,\n CooCollectionStatus,\n CooCreateBrandInput,\n CooCreateCollaborationInput,\n CooCreateCollectionInput,\n CooCreateLookInput,\n CooCreateLookbookInput,\n CooCreateStyleItemInput,\n CooCreateWardrobeItemInput,\n CooLook,\n CooLookConnection,\n CooLookOccasion,\n CooLookbook,\n CooStyleCategory,\n CooStyleItem,\n CooStyleItemConnection,\n CooStyleItemStatus,\n CooStyleProfile,\n CooUpdateBrandInput,\n CooUpdateCollectionInput,\n CooUpdateLookInput,\n CooUpdateStyleItemInput,\n CooUpdateWardrobeItemInput,\n CooUpsertStyleProfileInput,\n CooWardrobeItem,\n CooWardrobeSource,\n PaginationInput,\n} from '../types';\n\n// ---------------------------------------------------------------------------\n// Query key root\n// ---------------------------------------------------------------------------\n\nconst QK = 'coolandlovely';\n\n// ---------------------------------------------------------------------------\n// Low-level fetch wrapper\n// ---------------------------------------------------------------------------\n\nconst coolQuery = async <TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> => {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'GraphQL request failed');\n }\n if (!response.data) {\n throw new Error('GraphQL response did not include data');\n }\n // fe-libs graphqlFetch caches every *query* response for ~15s in a global\n // response cache. After a successful mutation we drop that cache so the\n // React Query `invalidateQueries` refetch reads fresh data instead of the\n // stale pre-mutation response (otherwise a like/publish/transition/remove\n // shows no change for up to 15s).\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlFetchResponseCache();\n }\n return response.data;\n};\n\n// ---------------------------------------------------------------------------\n// Reusable GraphQL field selections\n// ---------------------------------------------------------------------------\n\nconst BRAND_FIELDS = `\n id workspaceId name handle tagline bio aesthetics tags websiteUrl\n isFeatured status logo { id } cover { id } createdAt updatedAt\n`;\n\nconst COLLECTION_FIELDS = `\n id workspaceId brandId name description season status tags\n cover { id } publishAt endAt createdAt updatedAt\n`;\n\nconst STYLE_ITEM_FIELDS = `\n id workspaceId brandId collectionId name description category status\n sku price currency colours sizes materials tags image { id }\n createdAt updatedAt\n`;\n\nconst LOOK_FIELDS = `\n id workspaceId authorId title caption occasion visibility isFeatured\n likeCount saveCount collaborationId image { id } tags publishedAt\n createdAt updatedAt\n`;\n\nconst LOOK_ITEM_FIELDS = `\n id workspaceId lookId styleItemId position styling createdAt updatedAt\n styleItem { id name category price currency brand { id name handle } image { id } }\n`;\n\nconst COLLABORATION_FIELDS = `\n id workspaceId brandId creatorId collectionId title brief role status\n compensation currency tags startDate endDate publishedAt createdAt updatedAt\n`;\n\nconst WARDROBE_FIELDS = `\n id workspaceId profileId name category source brandName colour size notes\n styleItemId timesWorn lastWornAt tags image { id } createdAt updatedAt\n`;\n\nconst PROFILE_FIELDS = `\n id workspaceId ownerId displayName bio visibility stylePreferences\n palette favouriteBrands sizes createdAt updatedAt\n isFollowedByMe followerCount followingCount\n`;\n\nconst LOOKBOOK_FIELDS = `\n id workspaceId ownerId profileId name description visibility tags\n cover { id } createdAt updatedAt\n`;\n\n// ===========================================================================\n// Brands\n// ===========================================================================\n\nexport interface ListBrandsArgs {\n status?: CooBrandStatus;\n featured?: boolean;\n pagination?: PaginationInput;\n}\n\nexport const useBrands = (args: ListBrandsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'brands', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListBrands: CooBrandConnection }>(\n `query CooListBrands($status: CooBrandStatus, $featured: Boolean, $pagination: PaginationInput) {\n cooListBrands(status: $status, featured: $featured, pagination: $pagination) {\n items { ${BRAND_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n status: args.status,\n featured: args.featured,\n pagination: args.pagination ?? { limit: 50, offset: 0 },\n },\n apiGatewayUrl\n );\n return data.cooListBrands;\n },\n });\n};\n\nexport const useBrand = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'brand', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooBrand: CooBrand | null }>(\n `query CooBrand($id: ID!) {\n cooBrand(id: $id) {\n ${BRAND_FIELDS}\n collections { ${COLLECTION_FIELDS} }\n items { ${STYLE_ITEM_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooBrand;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateBrandInput) => {\n const data = await coolQuery<{ cooCreateBrand: CooBrand }>(\n `mutation CooCreateBrand($input: CooCreateBrandInput!) {\n cooCreateBrand(input: $input) { ${BRAND_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateBrand;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n },\n });\n};\n\nexport const useUpdateBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateBrandInput }) => {\n const data = await coolQuery<{ cooUpdateBrand: CooBrand }>(\n `mutation CooUpdateBrand($id: ID!, $input: CooUpdateBrandInput!) {\n cooUpdateBrand(id: $id, input: $input) { ${BRAND_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateBrand;\n },\n onSuccess: (brand) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'brand', brand.id] });\n },\n });\n};\n\nexport const useTransitionBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: CooBrandStatus }) => {\n const data = await coolQuery<{ cooTransitionBrand: CooBrand }>(\n `mutation CooTransitionBrand($id: ID!, $status: CooBrandStatus!) {\n cooTransitionBrand(id: $id, status: $status) { ${BRAND_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return data.cooTransitionBrand;\n },\n onSuccess: (brand) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'brand', brand.id] });\n },\n });\n};\n\nexport const useArchiveBrand = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooArchiveBrand: boolean }>(\n `mutation CooArchiveBrand($id: ID!) { cooArchiveBrand(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooArchiveBrand;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'brands'] });\n },\n });\n};\n\n// ===========================================================================\n// Collections\n// ===========================================================================\n\nexport interface ListCollectionsArgs {\n brandId?: string;\n status?: CooCollectionStatus;\n}\n\nexport const useCollections = (args: ListCollectionsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collections', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListCollections: CooCollection[] }>(\n `query CooListCollections($brandId: ID, $status: CooCollectionStatus) {\n cooListCollections(brandId: $brandId, status: $status) {\n ${COLLECTION_FIELDS}\n brand { id name handle }\n }\n }`,\n { brandId: args.brandId, status: args.status },\n apiGatewayUrl\n );\n return data.cooListCollections;\n },\n });\n};\n\nexport const useCollection = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collection', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooCollection: CooCollection | null }>(\n `query CooCollection($id: ID!) {\n cooCollection(id: $id) {\n ${COLLECTION_FIELDS}\n brand { id name handle }\n items { ${STYLE_ITEM_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooCollection;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateCollection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateCollectionInput) => {\n const data = await coolQuery<{ cooCreateCollection: CooCollection }>(\n `mutation CooCreateCollection($input: CooCreateCollectionInput!) {\n cooCreateCollection(input: $input) { ${COLLECTION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateCollection;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collections'] });\n },\n });\n};\n\nexport const useUpdateCollection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateCollectionInput }) => {\n const data = await coolQuery<{ cooUpdateCollection: CooCollection }>(\n `mutation CooUpdateCollection($id: ID!, $input: CooUpdateCollectionInput!) {\n cooUpdateCollection(id: $id, input: $input) { ${COLLECTION_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateCollection;\n },\n onSuccess: (collection) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collections'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'collection', collection.id] });\n },\n });\n};\n\nexport const usePublishCollection = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooPublishCollection: CooCollection }>(\n `mutation CooPublishCollection($id: ID!) {\n cooPublishCollection(id: $id) { ${COLLECTION_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooPublishCollection;\n },\n onSuccess: (collection) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collections'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'collection', collection.id] });\n },\n });\n};\n\n// ===========================================================================\n// Style Items\n// ===========================================================================\n\nexport interface ListStyleItemsArgs {\n brandId?: string;\n collectionId?: string;\n category?: CooStyleCategory;\n status?: CooStyleItemStatus;\n pagination?: PaginationInput;\n}\n\nexport const useStyleItems = (args: ListStyleItemsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'style-items', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListStyleItems: CooStyleItemConnection }>(\n `query CooListStyleItems(\n $brandId: ID, $collectionId: ID, $category: CooStyleCategory,\n $status: CooStyleItemStatus, $pagination: PaginationInput\n ) {\n cooListStyleItems(\n brandId: $brandId, collectionId: $collectionId, category: $category,\n status: $status, pagination: $pagination\n ) {\n items { ${STYLE_ITEM_FIELDS} brand { id name handle } }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n brandId: args.brandId,\n collectionId: args.collectionId,\n category: args.category,\n status: args.status,\n pagination: args.pagination ?? { limit: 50, offset: 0 },\n },\n apiGatewayUrl\n );\n return data.cooListStyleItems;\n },\n });\n};\n\nexport const useStyleItem = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'style-item', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooStyleItem: CooStyleItem | null }>(\n `query CooStyleItem($id: ID!) {\n cooStyleItem(id: $id) {\n ${STYLE_ITEM_FIELDS}\n brand { id name handle }\n collection { id name }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooStyleItem;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateStyleItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateStyleItemInput) => {\n const data = await coolQuery<{ cooCreateStyleItem: CooStyleItem }>(\n `mutation CooCreateStyleItem($input: CooCreateStyleItemInput!) {\n cooCreateStyleItem(input: $input) { ${STYLE_ITEM_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateStyleItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-items'] });\n },\n });\n};\n\nexport const useUpdateStyleItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateStyleItemInput }) => {\n const data = await coolQuery<{ cooUpdateStyleItem: CooStyleItem }>(\n `mutation CooUpdateStyleItem($id: ID!, $input: CooUpdateStyleItemInput!) {\n cooUpdateStyleItem(id: $id, input: $input) { ${STYLE_ITEM_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateStyleItem;\n },\n onSuccess: (item) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-items'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-item', item.id] });\n },\n });\n};\n\nexport const useArchiveStyleItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooArchiveStyleItem: boolean }>(\n `mutation CooArchiveStyleItem($id: ID!) { cooArchiveStyleItem(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooArchiveStyleItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'style-items'] });\n },\n });\n};\n\n// ===========================================================================\n// Looks\n// ===========================================================================\n\nexport interface ListLooksArgs {\n authorId?: string;\n occasion?: CooLookOccasion;\n featured?: boolean;\n pagination?: PaginationInput;\n}\n\nexport const useLooks = (args: ListLooksArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'looks', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListLooks: CooLookConnection }>(\n `query CooListLooks(\n $authorId: ID, $occasion: CooLookOccasion, $featured: Boolean, $pagination: PaginationInput\n ) {\n cooListLooks(authorId: $authorId, occasion: $occasion, featured: $featured, pagination: $pagination) {\n items { ${LOOK_FIELDS} }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n authorId: args.authorId,\n occasion: args.occasion,\n featured: args.featured,\n pagination: args.pagination ?? { limit: 50, offset: 0 },\n },\n apiGatewayUrl\n );\n return data.cooListLooks;\n },\n });\n};\n\nexport const useLook = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'look', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooLook: CooLook | null }>(\n `query CooLook($id: ID!) {\n cooLook(id: $id) {\n ${LOOK_FIELDS}\n items { ${LOOK_ITEM_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLook;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useLooksWithItems = (args: ListLooksArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'looks-with-items', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListLooks: CooLookConnection }>(\n `query CooListLooksWithItems(\n $authorId: ID, $occasion: CooLookOccasion, $featured: Boolean, $pagination: PaginationInput\n ) {\n cooListLooks(authorId: $authorId, occasion: $occasion, featured: $featured, pagination: $pagination) {\n items { ${LOOK_FIELDS} items { ${LOOK_ITEM_FIELDS} } }\n pageInfo { cursor hasMore }\n }\n }`,\n {\n authorId: args.authorId,\n occasion: args.occasion,\n featured: args.featured,\n pagination: args.pagination ?? { limit: 50, offset: 0 },\n },\n apiGatewayUrl\n );\n return data.cooListLooks;\n },\n });\n};\n\nexport const useCreateLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateLookInput) => {\n const data = await coolQuery<{ cooCreateLook: CooLook }>(\n `mutation CooCreateLook($input: CooCreateLookInput!) {\n cooCreateLook(input: $input) { ${LOOK_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateLook;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks-with-items'] });\n },\n });\n};\n\nexport const useUpdateLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateLookInput }) => {\n const data = await coolQuery<{ cooUpdateLook: CooLook }>(\n `mutation CooUpdateLook($id: ID!, $input: CooUpdateLookInput!) {\n cooUpdateLook(id: $id, input: $input) { ${LOOK_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateLook;\n },\n onSuccess: (look) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks-with-items'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'look', look.id] });\n },\n });\n};\n\nexport const usePublishLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooPublishLook: CooLook }>(\n `mutation CooPublishLook($id: ID!) { cooPublishLook(id: $id) { ${LOOK_FIELDS} } }`,\n { id },\n apiGatewayUrl\n );\n return data.cooPublishLook;\n },\n onSuccess: (look) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks-with-items'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'look', look.id] });\n },\n });\n};\n\nexport const useLikeLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooLikeLook: CooLook }>(\n `mutation CooLikeLook($id: ID!) { cooLikeLook(id: $id) { ${LOOK_FIELDS} } }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLikeLook;\n },\n onSuccess: (look) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks-with-items'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'look', look.id] });\n },\n });\n};\n\nexport const useDeleteLook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooDeleteLook: boolean }>(\n `mutation CooDeleteLook($id: ID!) { cooDeleteLook(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooDeleteLook;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'looks-with-items'] });\n },\n });\n};\n\n// ===========================================================================\n// Collaborations\n// ===========================================================================\n\nexport interface ListCollaborationsArgs {\n brandId?: string;\n creatorId?: string;\n status?: CooCollaborationStatus;\n}\n\nexport const useCollaborations = (args: ListCollaborationsArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collaborations', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListCollaborations: CooCollaboration[] }>(\n `query CooListCollaborations($brandId: ID, $creatorId: ID, $status: CooCollaborationStatus) {\n cooListCollaborations(brandId: $brandId, creatorId: $creatorId, status: $status) {\n ${COLLABORATION_FIELDS}\n brand { id name handle logo { id } }\n }\n }`,\n { brandId: args.brandId, creatorId: args.creatorId, status: args.status },\n apiGatewayUrl\n );\n return data.cooListCollaborations;\n },\n });\n};\n\nexport const useCollaboration = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'collaboration', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooCollaboration: CooCollaboration | null }>(\n `query CooCollaboration($id: ID!) {\n cooCollaboration(id: $id) {\n ${COLLABORATION_FIELDS}\n brand { id name handle logo { id } }\n looks { ${LOOK_FIELDS} }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooCollaboration;\n },\n enabled: Boolean(id),\n });\n};\n\nexport const useCreateCollaboration = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateCollaborationInput) => {\n const data = await coolQuery<{ cooCreateCollaboration: CooCollaboration }>(\n `mutation CooCreateCollaboration($input: CooCreateCollaborationInput!) {\n cooCreateCollaboration(input: $input) { ${COLLABORATION_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateCollaboration;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collaborations'] });\n },\n });\n};\n\nexport const useTransitionCollaboration = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, status }: { id: string; status: CooCollaborationStatus }) => {\n const data = await coolQuery<{ cooTransitionCollaboration: CooCollaboration }>(\n `mutation CooTransitionCollaboration($id: ID!, $status: CooCollaborationStatus!) {\n cooTransitionCollaboration(id: $id, status: $status) { ${COLLABORATION_FIELDS} }\n }`,\n { id, status },\n apiGatewayUrl\n );\n return data.cooTransitionCollaboration;\n },\n onSuccess: (collab) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'collaborations'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'collaboration', collab.id] });\n },\n });\n};\n\n// ===========================================================================\n// Wardrobe (self-scoped)\n// ===========================================================================\n\nexport interface ListWardrobeArgs {\n category?: CooStyleCategory;\n source?: CooWardrobeSource;\n}\n\nexport const useWardrobeItems = (args: ListWardrobeArgs = {}) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'wardrobe', args],\n queryFn: async () => {\n const data = await coolQuery<{ cooListWardrobeItems: CooWardrobeItem[] }>(\n `query CooListWardrobeItems($category: CooStyleCategory, $source: CooWardrobeSource) {\n cooListWardrobeItems(category: $category, source: $source) { ${WARDROBE_FIELDS} }\n }`,\n { category: args.category, source: args.source },\n apiGatewayUrl\n );\n return data.cooListWardrobeItems;\n },\n });\n};\n\nexport const useCreateWardrobeItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateWardrobeItemInput) => {\n const data = await coolQuery<{ cooCreateWardrobeItem: CooWardrobeItem }>(\n `mutation CooCreateWardrobeItem($input: CooCreateWardrobeItemInput!) {\n cooCreateWardrobeItem(input: $input) { ${WARDROBE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateWardrobeItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\nexport const useUpdateWardrobeItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async ({ id, input }: { id: string; input: CooUpdateWardrobeItemInput }) => {\n const data = await coolQuery<{ cooUpdateWardrobeItem: CooWardrobeItem }>(\n `mutation CooUpdateWardrobeItem($id: ID!, $input: CooUpdateWardrobeItemInput!) {\n cooUpdateWardrobeItem(id: $id, input: $input) { ${WARDROBE_FIELDS} }\n }`,\n { id, input },\n apiGatewayUrl\n );\n return data.cooUpdateWardrobeItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\nexport const useLogWardrobeWear = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooLogWardrobeWear: CooWardrobeItem }>(\n `mutation CooLogWardrobeWear($id: ID!) {\n cooLogWardrobeWear(id: $id) { ${WARDROBE_FIELDS} }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLogWardrobeWear;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\nexport const useRemoveWardrobeItem = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooRemoveWardrobeItem: boolean }>(\n `mutation CooRemoveWardrobeItem($id: ID!) { cooRemoveWardrobeItem(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooRemoveWardrobeItem;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'wardrobe'] });\n },\n });\n};\n\n// ===========================================================================\n// Style Profile (self-scoped)\n// ===========================================================================\n\nexport const useMyStyleProfile = () => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'my-style-profile'],\n queryFn: async () => {\n const data = await coolQuery<{ cooMyStyleProfile: CooStyleProfile | null }>(\n `query CooMyStyleProfile { cooMyStyleProfile { ${PROFILE_FIELDS} } }`,\n {},\n apiGatewayUrl\n );\n return data.cooMyStyleProfile;\n },\n });\n};\n\nexport const useUpsertStyleProfile = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooUpsertStyleProfileInput) => {\n const data = await coolQuery<{ cooUpsertStyleProfile: CooStyleProfile }>(\n `mutation CooUpsertStyleProfile($input: CooUpsertStyleProfileInput!) {\n cooUpsertStyleProfile(input: $input) { ${PROFILE_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooUpsertStyleProfile;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-style-profile'] });\n },\n });\n};\n\n// ===========================================================================\n// Lookbooks\n// ===========================================================================\n\nexport const useLookbooks = (ownerId?: string) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'lookbooks', ownerId],\n queryFn: async () => {\n const data = await coolQuery<{ cooListLookbooks: CooLookbook[] }>(\n `query CooListLookbooks($ownerId: ID) {\n cooListLookbooks(ownerId: $ownerId) {\n ${LOOKBOOK_FIELDS}\n entries { id workspaceId lookbookId lookId styleItemId note position createdAt updatedAt }\n }\n }`,\n { ownerId },\n apiGatewayUrl\n );\n return data.cooListLookbooks;\n },\n });\n};\n\nexport const useCreateLookbook = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooCreateLookbookInput) => {\n const data = await coolQuery<{ cooCreateLookbook: CooLookbook }>(\n `mutation CooCreateLookbook($input: CooCreateLookbookInput!) {\n cooCreateLookbook(input: $input) { ${LOOKBOOK_FIELDS} }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooCreateLookbook;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbooks'] });\n },\n });\n};\n\nexport const useAddLookbookEntry = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (input: CooAddLookbookEntryInput) => {\n const data = await coolQuery<{ cooAddLookbookEntry: { id: string; lookbookId: string } }>(\n `mutation CooAddLookbookEntry($input: CooAddLookbookEntryInput!) {\n cooAddLookbookEntry(input: $input) {\n id lookbookId lookId styleItemId note position createdAt updatedAt\n }\n }`,\n { input },\n apiGatewayUrl\n );\n return data.cooAddLookbookEntry;\n },\n onSuccess: (entry) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbooks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbook'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbook', entry.lookbookId] });\n },\n });\n};\n\nexport const useRemoveLookbookEntry = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (id: string) => {\n const data = await coolQuery<{ cooRemoveLookbookEntry: boolean }>(\n `mutation CooRemoveLookbookEntry($id: ID!) { cooRemoveLookbookEntry(id: $id) }`,\n { id },\n apiGatewayUrl\n );\n return data.cooRemoveLookbookEntry;\n },\n onSuccess: () => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbooks'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'lookbook'] });\n },\n });\n};\n\n// ===========================================================================\n// Follow graph (added this release — cooFollow / cooUnfollow / cooIsFollowing)\n// ===========================================================================\n\nconst FOLLOW_PROFILE_FIELDS = `\n id workspaceId ownerId displayName bio visibility\n stylePreferences palette favouriteBrands\n isFollowedByMe followerCount followingCount\n`;\n\n/** Whether the caller currently follows `followeeId`. */\nexport const useIsFollowing = (followeeId: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'is-following', followeeId],\n enabled: Boolean(followeeId),\n queryFn: async () => {\n const data = await coolQuery<{ cooIsFollowing: boolean }>(\n `query CooIsFollowing($followeeId: ID!) { cooIsFollowing(followeeId: $followeeId) }`,\n { followeeId },\n apiGatewayUrl\n );\n return data.cooIsFollowing;\n },\n });\n};\n\nexport const useFollow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (followeeId: string) => {\n const data = await coolQuery<{ cooFollow: boolean }>(\n `mutation CooFollow($followeeId: ID!) { cooFollow(followeeId: $followeeId) }`,\n { followeeId },\n apiGatewayUrl\n );\n return data.cooFollow;\n },\n onSuccess: (_result, followeeId) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'is-following', followeeId] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-following'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-followers'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-style-profile'] });\n },\n });\n};\n\nexport const useUnfollow = () => {\n const queryClient = useQueryClient();\n const { apiGatewayUrl } = useCoolAndLovely();\n return useMutation({\n mutationFn: async (followeeId: string) => {\n const data = await coolQuery<{ cooUnfollow: boolean }>(\n `mutation CooUnfollow($followeeId: ID!) { cooUnfollow(followeeId: $followeeId) }`,\n { followeeId },\n apiGatewayUrl\n );\n return data.cooUnfollow;\n },\n onSuccess: (_result, followeeId) => {\n void queryClient.invalidateQueries({ queryKey: [QK, 'is-following', followeeId] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-following'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-followers'] });\n void queryClient.invalidateQueries({ queryKey: [QK, 'my-style-profile'] });\n },\n });\n};\n\n/** Style profiles of people the caller follows. */\nexport const useMyFollowing = () => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'my-following'],\n queryFn: async () => {\n const data = await coolQuery<{ cooMyFollowing: CooStyleProfile[] }>(\n `query CooMyFollowing { cooMyFollowing { ${FOLLOW_PROFILE_FIELDS} } }`,\n {},\n apiGatewayUrl\n );\n return data.cooMyFollowing;\n },\n });\n};\n\n/** Style profiles of people following the caller. */\nexport const useMyFollowers = () => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'my-followers'],\n queryFn: async () => {\n const data = await coolQuery<{ cooMyFollowers: CooStyleProfile[] }>(\n `query CooMyFollowers { cooMyFollowers { ${FOLLOW_PROFILE_FIELDS} } }`,\n {},\n apiGatewayUrl\n );\n return data.cooMyFollowers;\n },\n });\n};\n\nexport const useLookbook = (id: string | undefined) => {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: [QK, 'lookbook', id],\n queryFn: async () => {\n const data = await coolQuery<{ cooLookbook: CooLookbook | null }>(\n `query CooLookbook($id: ID!) {\n cooLookbook(id: $id) {\n ${LOOKBOOK_FIELDS}\n entries { id workspaceId lookbookId lookId styleItemId note position createdAt updatedAt }\n }\n }`,\n { id },\n apiGatewayUrl\n );\n return data.cooLookbook;\n },\n enabled: Boolean(id),\n });\n};\n"],"mappings":"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"}
|
|
@@ -29,49 +29,66 @@ function d(e, t) {
|
|
|
29
29
|
let n = e.lastIndexOf(".");
|
|
30
30
|
return n > -1 && n < e.length - 1 ? e.slice(n + 1).toLowerCase() : (t.split("/")[1] ?? "bin").toLowerCase();
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
function f(e, t, n, r) {
|
|
33
|
+
return new Promise((i, a) => {
|
|
34
|
+
let o = new XMLHttpRequest();
|
|
35
|
+
o.open("PUT", e, !0), o.setRequestHeader("Content-Type", n), r && (o.upload.onprogress = (e) => {
|
|
36
|
+
let n = e.lengthComputable ? e.total : t.size, i = Math.min(e.loaded, n);
|
|
37
|
+
r({
|
|
38
|
+
loaded: i,
|
|
39
|
+
total: n,
|
|
40
|
+
pct: n > 0 ? Math.round(i / n * 100) : 0
|
|
41
|
+
});
|
|
42
|
+
}), o.onload = () => {
|
|
43
|
+
o.status >= 200 && o.status < 300 ? (r?.({
|
|
44
|
+
loaded: t.size,
|
|
45
|
+
total: t.size,
|
|
46
|
+
pct: 100
|
|
47
|
+
}), i()) : a(/* @__PURE__ */ Error(`Upload to storage failed (${o.status}).`));
|
|
48
|
+
}, o.onerror = () => a(/* @__PURE__ */ Error("Upload to storage failed — a network error occurred.")), o.onabort = () => a(/* @__PURE__ */ Error("Upload was cancelled.")), o.send(t);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async function p(e, t, n = a.items, r) {
|
|
52
|
+
let p = e.type || "application/octet-stream", m = String(e.size), h;
|
|
34
53
|
try {
|
|
35
|
-
|
|
54
|
+
h = (await o(s, { path: n }, t)).ensureFolderByPath?.id ?? void 0;
|
|
36
55
|
} catch {
|
|
37
|
-
|
|
56
|
+
h = void 0;
|
|
38
57
|
}
|
|
39
|
-
let
|
|
58
|
+
let g = await o(c, { input: {
|
|
40
59
|
fileName: e.name,
|
|
41
|
-
extension: d(e.name,
|
|
42
|
-
fileSize:
|
|
43
|
-
mimeType:
|
|
44
|
-
...
|
|
45
|
-
} }, t),
|
|
46
|
-
if (!
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
fileSize: f,
|
|
56
|
-
...p ? { folderId: p } : {}
|
|
57
|
-
} }, t), y = v.completeUpload?.file, b = v.completeUpload?.fileId ?? y?.id;
|
|
58
|
-
if (!b) throw Error("The upload could not be finalized.");
|
|
59
|
-
let x = "";
|
|
60
|
+
extension: d(e.name, p),
|
|
61
|
+
fileSize: m,
|
|
62
|
+
mimeType: p,
|
|
63
|
+
...h ? { folderId: h } : {}
|
|
64
|
+
} }, t), _ = g.initiateUpload?.uploadUrl, v = g.initiateUpload?.session.id;
|
|
65
|
+
if (!_ || !v) throw Error("The files service did not return an upload URL.");
|
|
66
|
+
await f(_, e, p, r);
|
|
67
|
+
let y = await o(l, { input: {
|
|
68
|
+
sessionId: v,
|
|
69
|
+
fileSize: m,
|
|
70
|
+
...h ? { folderId: h } : {}
|
|
71
|
+
} }, t), b = y.completeUpload?.file, x = y.completeUpload?.fileId ?? b?.id;
|
|
72
|
+
if (!x) throw Error("The upload could not be finalized.");
|
|
73
|
+
let S = "";
|
|
60
74
|
try {
|
|
61
|
-
|
|
62
|
-
id:
|
|
75
|
+
S = (await o(u, {
|
|
76
|
+
id: x,
|
|
63
77
|
expiresIn: i
|
|
64
78
|
}, t)).getFileDownloadUrl ?? "";
|
|
65
79
|
} catch {
|
|
66
|
-
|
|
80
|
+
S = "";
|
|
67
81
|
}
|
|
68
82
|
return {
|
|
69
|
-
id:
|
|
70
|
-
url:
|
|
71
|
-
name:
|
|
83
|
+
id: x,
|
|
84
|
+
url: S,
|
|
85
|
+
name: b?.name ?? e.name
|
|
72
86
|
};
|
|
73
87
|
}
|
|
74
|
-
function
|
|
88
|
+
async function m(e, t, n = a.items, r) {
|
|
89
|
+
return p(e, t, n, r);
|
|
90
|
+
}
|
|
91
|
+
function h(n) {
|
|
75
92
|
let { apiGatewayUrl: r } = e();
|
|
76
93
|
return t({
|
|
77
94
|
queryKey: [
|
|
@@ -89,6 +106,6 @@ function p(n) {
|
|
|
89
106
|
});
|
|
90
107
|
}
|
|
91
108
|
//#endregion
|
|
92
|
-
export { a as COO_FILE_FOLDERS,
|
|
109
|
+
export { a as COO_FILE_FOLDERS, m as uploadImageToFilesWithProgress, h as useFileUrl };
|
|
93
110
|
|
|
94
111
|
//# sourceMappingURL=useFiles.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFiles.js","names":[],"sources":["../../../src/coolandlovely/hooks/useFiles.ts"],"sourcesContent":["/**\n * File hooks — image upload + display via wspace-files-svc (NO duplication).\n * @module coolandlovely/hooks\n *\n * CoolAndLovely never stores blobs. Brand logos/covers, collection covers,\n * style-item photos, look photos, wardrobe photos and lookbook covers are\n * wspace-files `File` records referenced by id (the coo* inputs take *FileId;\n * the coo* types expose logo/cover/image: File resolved via federation).\n *\n * Upload uses the SAME graphqlFetch transport as every other coo* call — NOT\n * Apollo — driving the wspace-files 3-step presigned flow directly:\n * ensureFolderByPath -> initiateUpload -> HTTP PUT -> completeUpload\n * Display resolves a File id to a short-lived presigned URL via\n * getFileDownloadUrl.\n *\n * Two fleet gotchas are handled here:\n * (a) `fileSize` is REQUIRED on completeUpload even though this MFE's generated\n * `CompleteUploadInput` is stale and omits it. Because we send a hand-written\n * query with `Record<string, unknown>` variables (not typed against the\n * generated input) we simply include it and it works at runtime.\n * (b) `getFileDownloadUrl(expiresIn)` must be <= 604800 (S3 7-day cap → 500).\n * We use 21600 (6h); do NOT raise it.\n */\n\nimport { useQuery } from '@tanstack/react-query';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\nimport { useCoolAndLovely } from '../context';\n\n/**\n * 6 hours. getFileDownloadUrl rejects TTLs > 604800 (S3 7-day cap → 500); the\n * safe fleet ceiling is 21600. Do NOT raise this.\n */\nconst DOWNLOAD_URL_TTL_SECONDS = 21_600;\n\n/** Per-entity folders in wspace-files. */\nexport const COO_FILE_FOLDERS = {\n brands: '/CoolAndLovely/Brands',\n collections: '/CoolAndLovely/Collections',\n items: '/CoolAndLovely/Items',\n looks: '/CoolAndLovely/Looks',\n wardrobe: '/CoolAndLovely/Wardrobe',\n lookbooks: '/CoolAndLovely/Lookbooks',\n} as const;\n\nexport type CooFileFolder = (typeof COO_FILE_FOLDERS)[keyof typeof COO_FILE_FOLDERS];\n\nexport interface UploadedImage {\n id: string;\n url: string;\n name: string;\n}\n\n/** Thin wrapper over the workspace gateway using the MFE's proven transport. */\nasync function filesFetch<TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'Files request failed');\n }\n if (!response.data) {\n throw new Error('Files response did not include data');\n }\n // Drop fe-libs' ~15s query response cache after a mutation so a follow-up\n // getFileDownloadUrl reads fresh (mirrors coolQuery in useCoolAndLovelyApi).\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlFetchResponseCache();\n }\n return response.data;\n}\n\nconst ENSURE_FOLDER = `\n mutation CooEnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }\n`;\n\nconst INITIATE_UPLOAD = `\n mutation CooInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }\n`;\n\nconst COMPLETE_UPLOAD = `\n mutation CooCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file { id name }\n }\n }\n`;\n\nconst GET_FILE_DOWNLOAD_URL = `\n query CooGetFileDownloadUrl($id: ID!, $expiresIn: Int) {\n getFileDownloadUrl(id: $id, expiresIn: $expiresIn)\n }\n`;\n\nfunction fileExtension(name: string, mimeType: string): string {\n const dot = name.lastIndexOf('.');\n if (dot > -1 && dot < name.length - 1) return name.slice(dot + 1).toLowerCase();\n return (mimeType.split('/')[1] ?? 'bin').toLowerCase();\n}\n\n/**\n * Upload an image to wspace-files-svc and return its id + a presigned URL. The\n * caller persists `id` (e.g. cooUpdateBrand({ logoFileId })); `url` is for an\n * immediate preview. `baseUrl` is the shell's apiGatewayUrl.\n */\nexport async function uploadImageToFiles(\n file: File,\n baseUrl?: string,\n folderPath: string = COO_FILE_FOLDERS.items\n): Promise<UploadedImage> {\n const mimeType = file.type || 'application/octet-stream';\n const fileSize = String(file.size); // BigInt scalar — serialize as string.\n\n // 1. Resolve (or create) the destination folder. Best-effort: fall back to\n // the files root (folderId is optional on both initiate + complete).\n let folderId: string | undefined;\n try {\n const folder = await filesFetch<{ ensureFolderByPath?: { id: string } | null }>(\n ENSURE_FOLDER,\n { path: folderPath },\n baseUrl\n );\n folderId = folder.ensureFolderByPath?.id ?? undefined;\n } catch {\n folderId = undefined;\n }\n\n // 2. Open a presigned upload session.\n const initiated = await filesFetch<{\n initiateUpload?: { uploadUrl?: string | null; session: { id: string } } | null;\n }>(\n INITIATE_UPLOAD,\n {\n input: {\n fileName: file.name,\n extension: fileExtension(file.name, mimeType),\n fileSize, // required, BigInt-as-string\n mimeType,\n ...(folderId ? { folderId } : {}),\n },\n },\n baseUrl\n );\n const uploadUrl = initiated.initiateUpload?.uploadUrl;\n const sessionId = initiated.initiateUpload?.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('The files service did not return an upload URL.');\n }\n\n // 3. PUT the bytes straight to storage. Content-Type must match initiateUpload.\n const put = await fetch(uploadUrl, {\n method: 'PUT',\n body: file,\n headers: { 'Content-Type': mimeType },\n });\n if (!put.ok) {\n throw new Error(`Upload to storage failed (${put.status}).`);\n }\n\n // 4. Finalize — creates the File record. fileSize is REQUIRED here even though\n // the MFE's generated CompleteUploadInput is stale and omits it.\n const completed = await filesFetch<{\n completeUpload?: { fileId: string; file: { id: string; name: string } } | null;\n }>(\n COMPLETE_UPLOAD,\n {\n input: {\n sessionId,\n fileSize, // <-- stale-type gotcha: the live schema requires this\n ...(folderId ? { folderId } : {}),\n },\n },\n baseUrl\n );\n const record = completed.completeUpload?.file;\n const newId = completed.completeUpload?.fileId ?? record?.id;\n if (!newId) {\n throw new Error('The upload could not be finalized.');\n }\n\n // 5. Best-effort presigned URL for immediate preview.\n let url = '';\n try {\n const urlData = await filesFetch<{ getFileDownloadUrl?: string | null }>(\n GET_FILE_DOWNLOAD_URL,\n { id: newId, expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n baseUrl\n );\n url = urlData.getFileDownloadUrl ?? '';\n } catch {\n url = '';\n }\n\n return { id: newId, url, name: record?.name ?? file.name };\n}\n\n/** Resolve a wspace-files File id to a presigned URL for display. */\nexport function useFileUrl(fileId?: string | null) {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: ['coolandlovely', 'file-url', fileId ?? ''],\n enabled: Boolean(fileId),\n staleTime: 20 * 60 * 1000, // refetch well inside the 6h TTL\n retry: 1,\n queryFn: async () => {\n const data = await filesFetch<{ getFileDownloadUrl?: string | null }>(\n GET_FILE_DOWNLOAD_URL,\n { id: fileId, expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n apiGatewayUrl\n );\n return data.getFileDownloadUrl ?? null;\n },\n });\n}\n"],"mappings":";;;;;;AAkCA,IAAM,IAA2B,OAGpB,IAAmB;CAC9B,QAAQ;CACR,aAAa;CACb,OAAO;CACP,OAAO;CACP,UAAU;CACV,WAAW;CACZ;AAWD,eAAe,EACb,GACA,GACA,GACgB;CAChB,IAAM,IAAW,MAAM,EAAoB;EACzC,SAAS;EACT;EACA;EACA;EACA,gBAAgB;EACjB,CAAC;AACF,KAAI,EAAS,QAAQ,OACnB,OAAU,MAAM,EAAS,OAAO,IAAI,WAAW,uBAAuB;AAExE,KAAI,CAAC,EAAS,KACZ,OAAU,MAAM,sCAAsC;AAOxD,QAHI,iBAAiB,KAAK,EAAM,IAC9B,GAAgC,EAE3B,EAAS;;AAGlB,IAAM,IAAgB,qGAMhB,IAAkB,4JASlB,IAAkB,2JASlB,IAAwB;AAM9B,SAAS,EAAc,GAAc,GAA0B;CAC7D,IAAM,IAAM,EAAK,YAAY,IAAI;AAEjC,QADI,IAAM,MAAM,IAAM,EAAK,SAAS,IAAU,EAAK,MAAM,IAAM,EAAE,CAAC,aAAa,IACvE,EAAS,MAAM,IAAI,CAAC,MAAM,OAAO,aAAa;;AAQxD,eAAsB,EACpB,GACA,GACA,IAAqB,EAAiB,OACd;CACxB,IAAM,IAAW,EAAK,QAAQ,4BACxB,IAAW,OAAO,EAAK,KAAK,EAI9B;AACJ,KAAI;AAMF,OALe,MAAM,EACnB,GACA,EAAE,MAAM,GAAY,EACpB,EACD,EACiB,oBAAoB,MAAM,KAAA;SACtC;AACN,MAAW,KAAA;;CAIb,IAAM,IAAY,MAAM,EAGtB,GACA,EACE,OAAO;EACL,UAAU,EAAK;EACf,WAAW,EAAc,EAAK,MAAM,EAAS;EAC7C;EACA;EACA,GAAI,IAAW,EAAE,aAAU,GAAG,EAAE;EACjC,EACF,EACD,EACD,EACK,IAAY,EAAU,gBAAgB,WACtC,IAAY,EAAU,gBAAgB,QAAQ;AACpD,KAAI,CAAC,KAAa,CAAC,EACjB,OAAU,MAAM,kDAAkD;CAIpE,IAAM,IAAM,MAAM,MAAM,GAAW;EACjC,QAAQ;EACR,MAAM;EACN,SAAS,EAAE,gBAAgB,GAAU;EACtC,CAAC;AACF,KAAI,CAAC,EAAI,GACP,OAAU,MAAM,6BAA6B,EAAI,OAAO,IAAI;CAK9D,IAAM,IAAY,MAAM,EAGtB,GACA,EACE,OAAO;EACL;EACA;EACA,GAAI,IAAW,EAAE,aAAU,GAAG,EAAE;EACjC,EACF,EACD,EACD,EACK,IAAS,EAAU,gBAAgB,MACnC,IAAQ,EAAU,gBAAgB,UAAU,GAAQ;AAC1D,KAAI,CAAC,EACH,OAAU,MAAM,qCAAqC;CAIvD,IAAI,IAAM;AACV,KAAI;AAMF,OALgB,MAAM,EACpB,GACA;GAAE,IAAI;GAAO,WAAW;GAA0B,EAClD,EACD,EACa,sBAAsB;SAC9B;AACN,MAAM;;AAGR,QAAO;EAAE,IAAI;EAAO;EAAK,MAAM,GAAQ,QAAQ,EAAK;EAAM;;AAI5D,SAAgB,EAAW,GAAwB;CACjD,IAAM,EAAE,qBAAkB,GAAkB;AAC5C,QAAO,EAAS;EACd,UAAU;GAAC;GAAiB;GAAY,KAAU;GAAG;EACrD,SAAS,EAAQ;EACjB,WAAW,OAAU;EACrB,OAAO;EACP,SAAS,aACM,MAAM,EACjB,GACA;GAAE,IAAI;GAAQ,WAAW;GAA0B,EACnD,EACD,EACW,sBAAsB;EAErC,CAAC"}
|
|
1
|
+
{"version":3,"file":"useFiles.js","names":[],"sources":["../../../src/coolandlovely/hooks/useFiles.ts"],"sourcesContent":["/**\n * File hooks — image upload + display via wspace-files-svc (NO duplication).\n * @module coolandlovely/hooks\n *\n * CoolAndLovely never stores blobs. Brand logos/covers, collection covers,\n * style-item photos, look photos, wardrobe photos and lookbook covers are\n * wspace-files `File` records referenced by id (the coo* inputs take *FileId;\n * the coo* types expose logo/cover/image: File resolved via federation).\n *\n * Upload uses the SAME graphqlFetch transport as every other coo* call — NOT\n * Apollo — driving the wspace-files 3-step presigned flow directly:\n * ensureFolderByPath -> initiateUpload -> HTTP PUT -> completeUpload\n * Display resolves a File id to a short-lived presigned URL via\n * getFileDownloadUrl.\n *\n * Two fleet gotchas are handled here:\n * (a) `fileSize` is REQUIRED on completeUpload even though this MFE's generated\n * `CompleteUploadInput` is stale and omits it. Because we send a hand-written\n * query with `Record<string, unknown>` variables (not typed against the\n * generated input) we simply include it and it works at runtime.\n * (b) `getFileDownloadUrl(expiresIn)` must be <= 604800 (S3 7-day cap → 500).\n * We use 21600 (6h); do NOT raise it.\n */\n\nimport { useQuery } from '@tanstack/react-query';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { clearGraphqlFetchResponseCache } from '@burdenoff/fe-libs/shared/graphql/fetch';\n\nimport { useCoolAndLovely } from '../context';\n\n/**\n * 6 hours. getFileDownloadUrl rejects TTLs > 604800 (S3 7-day cap → 500); the\n * safe fleet ceiling is 21600. Do NOT raise this.\n */\nconst DOWNLOAD_URL_TTL_SECONDS = 21_600;\n\n/** Per-entity folders in wspace-files. */\nexport const COO_FILE_FOLDERS = {\n brands: '/CoolAndLovely/Brands',\n collections: '/CoolAndLovely/Collections',\n items: '/CoolAndLovely/Items',\n looks: '/CoolAndLovely/Looks',\n wardrobe: '/CoolAndLovely/Wardrobe',\n lookbooks: '/CoolAndLovely/Lookbooks',\n} as const;\n\nexport type CooFileFolder = (typeof COO_FILE_FOLDERS)[keyof typeof COO_FILE_FOLDERS];\n\nexport interface UploadedImage {\n id: string;\n url: string;\n name: string;\n}\n\n/** Thin wrapper over the workspace gateway using the MFE's proven transport. */\nasync function filesFetch<TData>(\n query: string,\n variables: Record<string, unknown>,\n baseUrl?: string\n): Promise<TData> {\n const response = await graphqlFetch<TData>({\n gateway: 'workspace',\n baseUrl,\n query,\n variables,\n workspaceToken: true,\n });\n if (response.errors?.length) {\n throw new Error(response.errors[0]?.message ?? 'Files request failed');\n }\n if (!response.data) {\n throw new Error('Files response did not include data');\n }\n // Drop fe-libs' ~15s query response cache after a mutation so a follow-up\n // getFileDownloadUrl reads fresh (mirrors coolQuery in useCoolAndLovelyApi).\n if (/^\\s*mutation\\b/.test(query)) {\n clearGraphqlFetchResponseCache();\n }\n return response.data;\n}\n\nconst ENSURE_FOLDER = `\n mutation CooEnsureFolder($path: String!) {\n ensureFolderByPath(path: $path) { id }\n }\n`;\n\nconst INITIATE_UPLOAD = `\n mutation CooInitiateUpload($input: InitiateUploadInput!) {\n initiateUpload(input: $input) {\n uploadUrl\n session { id }\n }\n }\n`;\n\nconst COMPLETE_UPLOAD = `\n mutation CooCompleteUpload($input: CompleteUploadInput!) {\n completeUpload(input: $input) {\n fileId\n file { id name }\n }\n }\n`;\n\nconst GET_FILE_DOWNLOAD_URL = `\n query CooGetFileDownloadUrl($id: ID!, $expiresIn: Int) {\n getFileDownloadUrl(id: $id, expiresIn: $expiresIn)\n }\n`;\n\nfunction fileExtension(name: string, mimeType: string): string {\n const dot = name.lastIndexOf('.');\n if (dot > -1 && dot < name.length - 1) return name.slice(dot + 1).toLowerCase();\n return (mimeType.split('/')[1] ?? 'bin').toLowerCase();\n}\n\n/** Upload progress emitted by {@link uploadImageToFilesWithProgress}. */\nexport interface UploadProgress {\n /** Bytes transferred so far. */\n loaded: number;\n /** Total bytes to transfer. */\n total: number;\n /** Rounded percentage complete, 0–100. */\n pct: number;\n}\n\n/**\n * PUT the bytes straight to storage via XMLHttpRequest so `upload.onprogress`\n * can surface a real progress bar (the presigned upload URL points at object\n * storage, so a plain PUT is all that is needed — no auth header). Content-Type\n * must match what was sent to `initiateUpload`.\n */\nfunction putWithProgress(\n uploadUrl: string,\n file: File,\n mimeType: string,\n onProgress?: (progress: UploadProgress) => void\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('PUT', uploadUrl, true);\n xhr.setRequestHeader('Content-Type', mimeType);\n if (onProgress) {\n xhr.upload.onprogress = (event: ProgressEvent) => {\n const total = event.lengthComputable ? event.total : file.size;\n const loaded = Math.min(event.loaded, total);\n const pct = total > 0 ? Math.round((loaded / total) * 100) : 0;\n onProgress({ loaded, total, pct });\n };\n }\n xhr.onload = () => {\n if (xhr.status >= 200 && xhr.status < 300) {\n onProgress?.({ loaded: file.size, total: file.size, pct: 100 });\n resolve();\n } else {\n reject(new Error(`Upload to storage failed (${xhr.status}).`));\n }\n };\n xhr.onerror = () => reject(new Error('Upload to storage failed — a network error occurred.'));\n xhr.onabort = () => reject(new Error('Upload was cancelled.'));\n xhr.send(file);\n });\n}\n\n/**\n * The full presigned upload flow (ensureFolder -> initiateUpload -> PUT ->\n * completeUpload -> download URL). Shared by both public upload helpers; the\n * PUT step reports progress when `onProgress` is supplied.\n */\nasync function runUpload(\n file: File,\n baseUrl?: string,\n folderPath: string = COO_FILE_FOLDERS.items,\n onProgress?: (progress: UploadProgress) => void\n): Promise<UploadedImage> {\n const mimeType = file.type || 'application/octet-stream';\n const fileSize = String(file.size); // BigInt scalar — serialize as string.\n\n // 1. Resolve (or create) the destination folder. Best-effort: fall back to\n // the files root (folderId is optional on both initiate + complete).\n let folderId: string | undefined;\n try {\n const folder = await filesFetch<{ ensureFolderByPath?: { id: string } | null }>(\n ENSURE_FOLDER,\n { path: folderPath },\n baseUrl\n );\n folderId = folder.ensureFolderByPath?.id ?? undefined;\n } catch {\n folderId = undefined;\n }\n\n // 2. Open a presigned upload session.\n const initiated = await filesFetch<{\n initiateUpload?: { uploadUrl?: string | null; session: { id: string } } | null;\n }>(\n INITIATE_UPLOAD,\n {\n input: {\n fileName: file.name,\n extension: fileExtension(file.name, mimeType),\n fileSize, // required, BigInt-as-string\n mimeType,\n ...(folderId ? { folderId } : {}),\n },\n },\n baseUrl\n );\n const uploadUrl = initiated.initiateUpload?.uploadUrl;\n const sessionId = initiated.initiateUpload?.session.id;\n if (!uploadUrl || !sessionId) {\n throw new Error('The files service did not return an upload URL.');\n }\n\n // 3. PUT the bytes straight to storage (XHR → progress). Content-Type must\n // match initiateUpload.\n await putWithProgress(uploadUrl, file, mimeType, onProgress);\n\n // 4. Finalize — creates the File record. fileSize is REQUIRED here even though\n // the MFE's generated CompleteUploadInput is stale and omits it.\n const completed = await filesFetch<{\n completeUpload?: { fileId: string; file: { id: string; name: string } } | null;\n }>(\n COMPLETE_UPLOAD,\n {\n input: {\n sessionId,\n fileSize, // <-- stale-type gotcha: the live schema requires this\n ...(folderId ? { folderId } : {}),\n },\n },\n baseUrl\n );\n const record = completed.completeUpload?.file;\n const newId = completed.completeUpload?.fileId ?? record?.id;\n if (!newId) {\n throw new Error('The upload could not be finalized.');\n }\n\n // 5. Best-effort presigned URL for immediate preview.\n let url = '';\n try {\n const urlData = await filesFetch<{ getFileDownloadUrl?: string | null }>(\n GET_FILE_DOWNLOAD_URL,\n { id: newId, expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n baseUrl\n );\n url = urlData.getFileDownloadUrl ?? '';\n } catch {\n url = '';\n }\n\n return { id: newId, url, name: record?.name ?? file.name };\n}\n\n/**\n * Upload an image to wspace-files-svc and return its id + a presigned URL. The\n * caller persists `id` (e.g. cooUpdateBrand({ logoFileId })); `url` is for an\n * immediate preview. `baseUrl` is the shell's apiGatewayUrl.\n */\nexport async function uploadImageToFiles(\n file: File,\n baseUrl?: string,\n folderPath: string = COO_FILE_FOLDERS.items\n): Promise<UploadedImage> {\n return runUpload(file, baseUrl, folderPath);\n}\n\n/**\n * Same as {@link uploadImageToFiles} but reports transfer progress via\n * `onProgress` (XHR `upload.onprogress`) so callers can render a live progress\n * bar. `baseUrl` is the shell's apiGatewayUrl.\n */\nexport async function uploadImageToFilesWithProgress(\n file: File,\n baseUrl?: string,\n folderPath: string = COO_FILE_FOLDERS.items,\n onProgress?: (progress: UploadProgress) => void\n): Promise<UploadedImage> {\n return runUpload(file, baseUrl, folderPath, onProgress);\n}\n\n/** Resolve a wspace-files File id to a presigned URL for display. */\nexport function useFileUrl(fileId?: string | null) {\n const { apiGatewayUrl } = useCoolAndLovely();\n return useQuery({\n queryKey: ['coolandlovely', 'file-url', fileId ?? ''],\n enabled: Boolean(fileId),\n staleTime: 20 * 60 * 1000, // refetch well inside the 6h TTL\n retry: 1,\n queryFn: async () => {\n const data = await filesFetch<{ getFileDownloadUrl?: string | null }>(\n GET_FILE_DOWNLOAD_URL,\n { id: fileId, expiresIn: DOWNLOAD_URL_TTL_SECONDS },\n apiGatewayUrl\n );\n return data.getFileDownloadUrl ?? null;\n },\n });\n}\n"],"mappings":"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"}
|