@burdenoff/microfe-coolandlovely 2026.628.1 → 2026.706.1
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/components/Field.js.map +1 -1
- package/dist/coolandlovely/components/StateViews.js.map +1 -1
- package/dist/coolandlovely/context/CoolAndLovelyContext.js.map +1 -1
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js +149 -148
- package/dist/coolandlovely/hooks/useCoolAndLovelyApi.js.map +1 -1
- package/dist/coolandlovely/pages/HomePage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandFormPage.js +1 -1
- package/dist/coolandlovely/pages/brands/BrandFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js +5 -1
- package/dist/coolandlovely/pages/brands/BrandsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collaborations/CollaborationsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/collections/CollectionsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/items/StyleItemsListPage.js.map +1 -1
- package/dist/coolandlovely/pages/lookbooks/LookbooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookDetailPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LookFormPage.js.map +1 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.js +5 -1
- package/dist/coolandlovely/pages/looks/LooksListPage.js.map +1 -1
- package/dist/coolandlovely/pages/wardrobe/WardrobeFormPage.js.map +1 -1
- 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\nimport { useCoolAndLovely } from '../context';\nimport type {\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 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`;\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 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\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 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`;\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 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\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport {\n useBrands,\n useStyleItems,\n useLooks,\n useCollaborations,\n useWardrobeItems,\n} from '../hooks';\nimport { formatRelativeTime, titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\nimport type { DashboardStats } from '../types';\n\ninterface QuickAction {\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n];\n\nconst modules = [\n {\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 6), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <LoadingView label=\"Loading your style command center…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <ErrorView\n title=\"Could not load CoolAndLovely\"\n message=\"The fashion service did not respond. Please try again.\"\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n label: 'Brands',\n sub: `${stats.publishedBrands} published`,\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n label: 'Style Items',\n sub: `${stats.activeStyleItems} active`,\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n label: 'Looks',\n sub: `${stats.featuredLooks} featured`,\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n label: 'Active Collabs',\n sub: `${stats.wardrobeItems} in wardrobe`,\n },\n ];\n\n return (\n <PageLayout\n title={`Hello, ${firstName}`}\n description=\"Your fashion & lifestyle command center\"\n hero\n >\n <PagePurpose className=\"mb-6\">\n CoolAndLovely connects the three sides of style on one workspace-native model: brands launch\n storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators\n publish shop-the-look outfits and run brand collaborations. Jump into any module below — all\n data is live from the CoolAndLovely service.\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m) => (\n <MetricsCard\n key={m.label}\n icon={m.icon}\n value={m.value}\n label={m.label}\n trendValue={m.sub}\n trend=\"neutral\"\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Quick actions</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{action.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{action.description}</p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Explore</h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.label}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn(\n 'flex h-12 w-12 items-center justify-center rounded-full',\n mod.color\n )}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{mod.description}</p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Latest looks</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n No looks published yet. Be the first to publish a shop-the-look outfit.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-6\">\n {latestLooks.map((look) => (\n <button\n key={look.id}\n type=\"button\"\n onClick={() => navigateTo(`/looks/${look.id}`)}\n className=\"group overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Sparkles className=\"h-7 w-7\" />}\n seed={look.id}\n className=\"h-28 w-full\"\n />\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{look.title}</p>\n <div className=\"mt-1.5 flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">{titleCase(look.occasion)}</span>\n <span className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Heart className=\"h-3 w-3\" /> {look.likeCount}\n </span>\n </div>\n </div>\n </button>\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently updated brands</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"flex w-full items-center gap-4 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n className=\"h-11 w-11 shrink-0\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n <span className=\"hidden shrink-0 text-xs text-text-secondary sm:inline\">\n {formatRelativeTime(brand.updatedAt)}\n </span>\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HomePage.js","names":[],"sources":["../../../src/coolandlovely/pages/HomePage.tsx"],"sourcesContent":["/**\n * Overview / Home Page\n * @module coolandlovely/pages\n *\n * The CoolAndLovely command center landing page: a style-operating-system\n * dashboard rolling up live catalog (brands, items), social (looks,\n * collaborations) and the shopper's own wardrobe — all from real `coo*` ops.\n */\n\nimport { useMemo } from 'react';\nimport {\n Store,\n Shirt,\n Sparkles,\n Handshake,\n Shapes,\n Heart,\n ArrowRight,\n Star,\n Plus,\n BookHeart,\n UserCircle,\n} from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../components/PageLayout';\nimport { MetricsCard } from '../components/MetricsCard';\nimport { StatusBadge } from '../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../components/StateViews';\nimport { Thumbnail } from '../components/Thumbnail';\nimport { useCoolAndLovely } from '../context/CoolAndLovelyContext';\nimport { useBrands, useStyleItems, useLooks, useCollaborations, useWardrobeItems } from '../hooks';\nimport { formatRelativeTime, titleCase } from '../utils/formatters';\nimport { cn } from '../utils/cn';\nimport type { DashboardStats } from '../types';\n\ninterface QuickAction {\n label: string;\n description: string;\n icon: React.ReactNode;\n path: string;\n color: string;\n}\n\nconst quickActions: QuickAction[] = [\n {\n label: 'New Brand',\n description: 'Launch a storefront',\n icon: <Store className=\"h-5 w-5\" />,\n path: '/brands/new',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Add Style Item',\n description: 'List a piece',\n icon: <Shirt className=\"h-5 w-5\" />,\n path: '/items/new',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Publish a Look',\n description: 'Shop-the-look outfit',\n icon: <Sparkles className=\"h-5 w-5\" />,\n path: '/looks/new',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'My Wardrobe',\n description: 'What you own',\n icon: <Heart className=\"h-5 w-5\" />,\n path: '/wardrobe',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n];\n\nconst modules = [\n {\n label: 'Brands',\n description: 'Storefronts with a publish lifecycle',\n icon: <Store className=\"h-6 w-6\" />,\n path: '/brands',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Collections',\n description: 'Seasonal drops & curated edits',\n icon: <Shapes className=\"h-6 w-6\" />,\n path: '/collections',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n {\n label: 'Style Items',\n description: 'The shoppable catalogue',\n icon: <Shirt className=\"h-6 w-6\" />,\n path: '/items',\n color: 'bg-[var(--color-accent-soft)] text-text-primary',\n },\n {\n label: 'Looks',\n description: 'Published shop-the-look outfits',\n icon: <Sparkles className=\"h-6 w-6\" />,\n path: '/looks',\n color: 'bg-status-success-bg-subtle text-status-success-text',\n },\n {\n label: 'Collaborations',\n description: 'Brand × creator partnerships',\n icon: <Handshake className=\"h-6 w-6\" />,\n path: '/collaborations',\n color: 'bg-status-warning-bg-subtle text-status-warning-text',\n },\n {\n label: 'Wardrobe',\n description: 'Your digital closet & wear log',\n icon: <Heart className=\"h-6 w-6\" />,\n path: '/wardrobe',\n color: 'bg-status-error-bg-subtle text-status-error-text',\n },\n {\n label: 'Lookbooks',\n description: 'Themed boards of saved pieces',\n icon: <BookHeart className=\"h-6 w-6\" />,\n path: '/lookbooks',\n color: 'bg-accent-purple-subtle text-text-primary',\n },\n {\n label: 'Style Profile',\n description: 'Your taste, palette & sizing',\n icon: <UserCircle className=\"h-6 w-6\" />,\n path: '/profile',\n color: 'bg-accent-blue-subtle text-accent-blue',\n },\n];\n\nexport function HomePage() {\n const { navigateTo, currentUser } = useCoolAndLovely();\n\n const brands = useBrands();\n const items = useStyleItems();\n const looks = useLooks();\n const collaborations = useCollaborations();\n const wardrobe = useWardrobeItems();\n\n const firstName = currentUser?.firstName ?? currentUser?.name ?? 'there';\n\n const isLoading =\n brands.isLoading ||\n items.isLoading ||\n looks.isLoading ||\n collaborations.isLoading ||\n wardrobe.isLoading;\n\n const isError = brands.isError && items.isError && looks.isError && collaborations.isError;\n\n const stats = useMemo<DashboardStats>(() => {\n const brandItems = brands.data?.items ?? [];\n const styleItems = items.data?.items ?? [];\n const lookItems = looks.data?.items ?? [];\n const collabs = collaborations.data ?? [];\n const wardrobeItems = wardrobe.data ?? [];\n return {\n totalBrands: brandItems.length,\n publishedBrands: brandItems.filter((b) => b.status === 'PUBLISHED').length,\n totalStyleItems: styleItems.length,\n activeStyleItems: styleItems.filter((i) => i.status === 'ACTIVE').length,\n totalLooks: lookItems.length,\n featuredLooks: lookItems.filter((l) => l.isFeatured).length,\n activeCollaborations: collabs.filter(\n (c) => c.status === 'ACCEPTED' || c.status === 'IN_PROGRESS'\n ).length,\n wardrobeItems: wardrobeItems.length,\n };\n }, [brands.data, items.data, looks.data, collaborations.data, wardrobe.data]);\n\n const latestLooks = useMemo(() => [...(looks.data?.items ?? [])].slice(0, 6), [looks.data]);\n\n const recentBrands = useMemo(\n () =>\n [...(brands.data?.items ?? [])]\n .sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n .slice(0, 4),\n [brands.data]\n );\n\n if (isLoading) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <LoadingView label=\"Loading your style command center…\" />\n </PageLayout>\n );\n }\n\n if (isError) {\n return (\n <PageLayout title=\"CoolAndLovely\">\n <ErrorView\n title=\"Could not load CoolAndLovely\"\n message=\"The fashion service did not respond. Please try again.\"\n onRetry={() => {\n void brands.refetch();\n void items.refetch();\n void looks.refetch();\n void collaborations.refetch();\n void wardrobe.refetch();\n }}\n />\n </PageLayout>\n );\n }\n\n const metrics = [\n {\n icon: <Store className=\"h-5 w-5\" />,\n value: stats.totalBrands.toString(),\n label: 'Brands',\n sub: `${stats.publishedBrands} published`,\n },\n {\n icon: <Shirt className=\"h-5 w-5\" />,\n value: stats.totalStyleItems.toString(),\n label: 'Style Items',\n sub: `${stats.activeStyleItems} active`,\n },\n {\n icon: <Sparkles className=\"h-5 w-5\" />,\n value: stats.totalLooks.toString(),\n label: 'Looks',\n sub: `${stats.featuredLooks} featured`,\n },\n {\n icon: <Handshake className=\"h-5 w-5\" />,\n value: stats.activeCollaborations.toString(),\n label: 'Active Collabs',\n sub: `${stats.wardrobeItems} in wardrobe`,\n },\n ];\n\n return (\n <PageLayout\n title={`Hello, ${firstName}`}\n description=\"Your fashion & lifestyle command center\"\n hero\n >\n <PagePurpose className=\"mb-6\">\n CoolAndLovely connects the three sides of style on one workspace-native model: brands launch\n storefronts and seasonal collections, you build a wardrobe and lookbooks, and creators\n publish shop-the-look outfits and run brand collaborations. Jump into any module below — all\n data is live from the CoolAndLovely service.\n </PagePurpose>\n\n {/* Headline metrics — the one emphasis zone for this page */}\n <EmphasisPanel className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">At a glance</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {metrics.map((m) => (\n <MetricsCard\n key={m.label}\n icon={m.icon}\n value={m.value}\n label={m.label}\n trendValue={m.sub}\n trend=\"neutral\"\n />\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Quick Actions */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Quick actions</h2>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {quickActions.map((action) => (\n <button\n key={action.label}\n type=\"button\"\n onClick={() => navigateTo(action.path)}\n className={cn(\n 'flex items-start gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 text-left shadow-sm transition-all',\n 'hover:border-accent-foreground/20 hover:shadow-md'\n )}\n >\n <div\n className={cn(\n 'flex h-10 w-10 shrink-0 items-center justify-center rounded-lg',\n action.color\n )}\n >\n {action.icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-semibold text-text-primary\">{action.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{action.description}</p>\n </div>\n <Plus className=\"mt-1 h-4 w-4 shrink-0 text-text-secondary\" />\n </button>\n ))}\n </div>\n </section>\n\n {/* Modules */}\n <section className=\"mb-8\">\n <h2 className=\"mb-4 text-lg font-semibold text-text-primary\">Explore</h2>\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-4\">\n {modules.map((mod) => (\n <button\n key={mod.label}\n type=\"button\"\n onClick={() => navigateTo(mod.path)}\n className=\"flex flex-col gap-3 rounded-lg border border-border-seam bg-bg-surface p-5 text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <div\n className={cn('flex h-12 w-12 items-center justify-center rounded-full', mod.color)}\n >\n {mod.icon}\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{mod.label}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{mod.description}</p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {/* Latest looks */}\n <section className=\"mb-8\">\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Latest looks</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/looks')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n {latestLooks.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-6 text-sm text-text-secondary\">\n No looks published yet. Be the first to publish a shop-the-look outfit.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-6\">\n {latestLooks.map((look) => (\n <button\n key={look.id}\n type=\"button\"\n onClick={() => navigateTo(`/looks/${look.id}`)}\n className=\"group overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Sparkles className=\"h-7 w-7\" />}\n seed={look.id}\n className=\"h-28 w-full\"\n />\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{look.title}</p>\n <div className=\"mt-1.5 flex items-center justify-between\">\n <span className=\"text-xs text-text-secondary\">{titleCase(look.occasion)}</span>\n <span className=\"flex items-center gap-1 text-xs text-text-secondary\">\n <Heart className=\"h-3 w-3\" /> {look.likeCount}\n </span>\n </div>\n </div>\n </button>\n ))}\n </div>\n )}\n </section>\n\n {/* Recent brands */}\n {recentBrands.length > 0 && (\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold text-text-primary\">Recently updated brands</h2>\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"flex items-center gap-1 text-sm font-medium text-primary hover:underline\"\n >\n View all <ArrowRight className=\"h-3.5 w-3.5\" />\n </button>\n </div>\n <div className=\"divide-y divide-border-seam rounded-lg border border-border-seam bg-bg-surface\">\n {recentBrands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"flex w-full items-center gap-4 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <Thumbnail\n icon={<Store className=\"h-5 w-5\" />}\n seed={brand.id}\n className=\"h-11 w-11 shrink-0\"\n />\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{brand.name}</p>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n <span className=\"hidden shrink-0 text-xs text-text-secondary sm:inline\">\n {formatRelativeTime(brand.updatedAt)}\n </span>\n </button>\n ))}\n </div>\n </section>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrandDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/brands/BrandDetailPage.tsx"],"sourcesContent":["/**\n * Brand Detail Page\n * @module coolandlovely/pages/brands\n *\n * Renders a single brand storefront (`cooBrand`) with its publish-lifecycle\n * transitions (`cooTransitionBrand`), collections and style items.\n */\n\nimport { useParams } from 'react-router-dom';\nimport {\n Store,\n Star,\n ExternalLink,\n Pencil,\n ArrowLeft,\n Shapes,\n Shirt,\n Loader2,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useBrand, useTransitionBrand } from '../../hooks';\nimport { categoryIcon } from '../../utils/icons';\nimport { formatPrice, formatDate, titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooBrandStatus } from '../../types';\n\n/** Allowed next states per the svc brand state machine (UX guidance only;\n * the service is the authority and rejects illegal jumps). */\nconst NEXT_STATES: Record<CooBrandStatus, CooBrandStatus[]> = {\n DRAFT: ['PENDING_REVIEW', 'ARCHIVED'],\n PENDING_REVIEW: ['PUBLISHED', 'DRAFT', 'ARCHIVED'],\n PUBLISHED: ['SUSPENDED', 'ARCHIVED'],\n SUSPENDED: ['PUBLISHED', 'ARCHIVED'],\n ARCHIVED: [],\n};\n\nexport function BrandDetailPage() {\n const { brandId } = useParams<{ brandId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useBrand(brandId);\n const transition = useTransitionBrand();\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Brand\">\n <LoadingView label=\"Loading brand…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Brand\">\n <ErrorView\n title=\"Brand not found\"\n message=\"This storefront could not be loaded or does not exist.\"\n onRetry={() => void query.refetch()}\n />\n </PageLayout>\n );\n }\n\n const brand = query.data;\n const collections = brand.collections ?? [];\n const items = brand.items ?? [];\n const nextStates = NEXT_STATES[brand.status];\n\n return (\n <PageLayout\n title={brand.name}\n description={`@${brand.handle}`}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}/edit`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Pencil className=\"h-4 w-4\" /> Edit\n </button>\n </div>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"lg:col-span-2\">\n <GlassCard treatment=\"glass\" className=\"overflow-hidden p-0\">\n <Thumbnail icon={<Store className=\"h-12 w-12\" />} seed={brand.id} className=\"h-44 w-full\" />\n <div className=\"p-6\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{brand.name}</h2>\n {brand.isFeatured && (\n <Star className=\"h-4 w-4 fill-status-warning-text text-status-warning-text\" />\n )}\n <StatusBadge status={brand.status} className=\"ml-auto\" />\n </div>\n {brand.tagline && <p className=\"mt-2 text-sm text-text-primary\">{brand.tagline}</p>}\n {brand.bio && <p className=\"mt-3 text-sm text-text-secondary\">{brand.bio}</p>}\n\n {brand.aesthetics.length > 0 && (\n <div className=\"mt-4\">\n <p className=\"mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Aesthetics\n </p>\n <TagList tags={brand.aesthetics.map(titleCase)} tone=\"accent\" max={12} />\n </div>\n )}\n {brand.tags.length > 0 && (\n <div className=\"mt-3\">\n <p className=\"mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Tags\n </p>\n <TagList tags={brand.tags} max={12} />\n </div>\n )}\n {brand.websiteUrl && (\n <a\n href={brand.websiteUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-4 inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline\"\n >\n <ExternalLink className=\"h-4 w-4\" /> {brand.websiteUrl}\n </a>\n )}\n </div>\n </GlassCard>\n\n {/* Collections */}\n <section className=\"mt-6\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Shapes className=\"h-5 w-5\" /> Collections ({collections.length})\n </h3>\n </div>\n {collections.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n No collections yet.\n </p>\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {collections.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => navigateTo(`/collections/${c.id}`)}\n className=\"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <Thumbnail icon={<Shapes className=\"h-5 w-5\" />} seed={c.id} className=\"h-11 w-11 shrink-0\" />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{c.name}</p>\n <p className=\"text-xs text-text-secondary\">{titleCase(c.season)}</p>\n </div>\n <StatusBadge status={c.status} />\n </button>\n ))}\n </div>\n )}\n </section>\n\n {/* Items */}\n <section className=\"mt-6\">\n <h3 className=\"mb-3 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Shirt className=\"h-5 w-5\" /> Style items ({items.length})\n </h3>\n {items.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n No items yet.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3\">\n {items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => navigateTo(`/items/${item.id}`)}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail icon={categoryIcon(item.category, 24)} seed={item.id} className=\"h-24 w-full\" />\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{item.name}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatPrice(item.price, item.currency)}\n </p>\n </div>\n </button>\n ))}\n </div>\n )}\n </section>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-4\">\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Lifecycle</h3>\n <div className=\"mb-3 flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Current:</span>\n <StatusBadge status={brand.status} />\n </div>\n {nextStates.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">This brand is archived (terminal state).</p>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {nextStates.map((next) => (\n <button\n key={next}\n type=\"button\"\n disabled={transition.isPending}\n onClick={() => transition.mutate({ id: brand.id, status: next })}\n className={cn(\n 'inline-flex items-center justify-center gap-2 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50'\n )}\n >\n {transition.isPending && <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />}\n Move to {titleCase(next)}\n </button>\n ))}\n </div>\n )}\n {transition.isError && (\n <p className=\"mt-2 text-xs text-status-error-text\">\n {transition.error instanceof Error ? transition.error.message : 'Transition failed'}\n </p>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Details</h3>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Handle</dt>\n <dd className=\"text-text-primary\">@{brand.handle}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Featured</dt>\n <dd className=\"text-text-primary\">{brand.isFeatured ? 'Yes' : 'No'}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-text-primary\">{formatDate(brand.createdAt, { hour: undefined, minute: undefined })}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Updated</dt>\n <dd className=\"text-text-primary\">{formatDate(brand.updatedAt, { hour: undefined, minute: undefined })}</dd>\n </div>\n </dl>\n </GlassCard>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAkCA,IAAM,IAAwD;CAC5D,OAAO,CAAC,kBAAkB,WAAW;CACrC,gBAAgB;EAAC;EAAa;EAAS;EAAW;CAClD,WAAW,CAAC,aAAa,WAAW;CACpC,WAAW,CAAC,aAAa,WAAW;CACpC,UAAU,EAAE;CACb;AAED,SAAgB,IAAkB;CAChC,IAAM,EAAE,eAAY,GAAgC,EAC9C,EAAE,kBAAe,GAAkB,EACnC,IAAQ,EAAS,EAAQ,EACzB,IAAa,GAAoB;AAEvC,KAAI,EAAM,UACR,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD,EAAa,OAAM,kBAAmB,CAAA;EAC3B,CAAA;AAIjB,KAAI,EAAM,WAAW,CAAC,EAAM,KAC1B,QACE,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GACE,OAAM;GACN,SAAQ;GACR,eAAe,KAAK,EAAM,SAAS;GACnC,CAAA;EACS,CAAA;CAIjB,IAAM,IAAQ,EAAM,MACd,IAAc,EAAM,eAAe,EAAE,EACrC,IAAQ,EAAM,SAAS,EAAE,EACzB,IAAa,EAAY,EAAM;AAErC,QACE,kBAAC,GAAD;EACE,OAAO,EAAM;EACb,aAAa,IAAI,EAAM;EACvB,SACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,UAAU;IACpC,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,QAC1B;OACT,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAW,WAAW,EAAM,GAAG,OAAO;IACrD,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA,EAAA,QACvB;MACL;;YAGR,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAW,WAAU;MAAQ,WAAU;gBAAvC,CACE,kBAAC,GAAD;OAAW,MAAM,kBAAC,GAAD,EAAO,WAAU,aAAc,CAAA;OAAE,MAAM,EAAM;OAAI,WAAU;OAAgB,CAAA,EAC5F,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,MAAD;WAAI,WAAU;qBAA2C,EAAM;WAAU,CAAA;UACxE,EAAM,cACL,kBAAC,GAAD,EAAM,WAAU,6DAA8D,CAAA;UAEhF,kBAAC,GAAD;WAAa,QAAQ,EAAM;WAAQ,WAAU;WAAY,CAAA;UACrD;;QACL,EAAM,WAAW,kBAAC,KAAD;SAAG,WAAU;mBAAkC,EAAM;SAAY,CAAA;QAClF,EAAM,OAAO,kBAAC,KAAD;SAAG,WAAU;mBAAoC,EAAM;SAAQ,CAAA;QAE5E,EAAM,WAAW,SAAS,KACzB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAyE;UAElF,CAAA,EACJ,kBAAC,GAAD;UAAS,MAAM,EAAM,WAAW,IAAI,EAAU;UAAE,MAAK;UAAS,KAAK;UAAM,CAAA,CACrE;;QAEP,EAAM,KAAK,SAAS,KACnB,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAyE;UAElF,CAAA,EACJ,kBAAC,GAAD;UAAS,MAAM,EAAM;UAAM,KAAK;UAAM,CAAA,CAClC;;QAEP,EAAM,cACL,kBAAC,KAAD;SACE,MAAM,EAAM;SACZ,QAAO;SACP,KAAI;SACJ,WAAU;mBAJZ;UAME,kBAAC,GAAD,EAAc,WAAU,WAAY,CAAA;;UAAE,EAAM;UAC1C;;QAEF;SACI;;KAGZ,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,MAAD;QAAI,WAAU;kBAAd;SACE,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;;SAAe,EAAY;SAAO;SAC7D;;OACD,CAAA,EACL,EAAY,WAAW,IACtB,kBAAC,KAAD;OAAG,WAAU;iBAAqF;OAE9F,CAAA,GAEJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAY,KAAK,MAChB,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAW,gBAAgB,EAAE,KAAK;QACjD,WAAU;kBAJZ;SAME,kBAAC,GAAD;UAAW,MAAM,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;UAAE,MAAM,EAAE;UAAI,WAAU;UAAuB,CAAA;SAC9F,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,KAAD;WAAG,WAAU;qBAAkD,EAAE;WAAS,CAAA,EAC1E,kBAAC,KAAD;WAAG,WAAU;qBAA+B,EAAU,EAAE,OAAO;WAAK,CAAA,CAChE;;SACN,kBAAC,GAAD,EAAa,QAAQ,EAAE,QAAU,CAAA;SAC1B;UAXF,EAAE,GAWA,CACT;OACE,CAAA,CAEA;;KAGV,kBAAC,WAAD;MAAS,WAAU;gBAAnB,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAd;QACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;;QAAe,EAAM;QAAO;QACtD;UACJ,EAAM,WAAW,IAChB,kBAAC,KAAD;OAAG,WAAU;iBAAqF;OAE9F,CAAA,GAEJ,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAM,KAAK,MACV,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAW,UAAU,EAAK,KAAK;QAC9C,WAAU;kBAJZ,CAME,kBAAC,GAAD;SAAW,MAAM,EAAa,EAAK,UAAU,GAAG;SAAE,MAAM,EAAK;SAAI,WAAU;SAAgB,CAAA,EAC3F,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAkD,EAAK;UAAS,CAAA,EAC7E,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAY,EAAK,OAAO,EAAK,SAAS;UACrC,CAAA,CACA;WACC;UAZF,EAAK,GAYH,CACT;OACE,CAAA,CAEA;;KACN;OAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAU,WAAU;eAAzC;MACE,kBAAC,MAAD;OAAI,WAAU;iBAA+C;OAAc,CAAA;MAC3E,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAA8B;QAAe,CAAA,EAC7D,kBAAC,GAAD,EAAa,QAAQ,EAAM,QAAU,CAAA,CACjC;;MACL,EAAW,WAAW,IACrB,kBAAC,KAAD;OAAG,WAAU;iBAA8B;OAA4C,CAAA,GAEvF,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAW,KAAK,MACf,kBAAC,UAAD;QAEE,MAAK;QACL,UAAU,EAAW;QACrB,eAAe,EAAW,OAAO;SAAE,IAAI,EAAM;SAAI,QAAQ;SAAM,CAAC;QAChE,WAAW,EACT,8LACD;kBAPH;SASG,EAAW,aAAa,kBAAC,GAAD,EAAS,WAAU,4BAA6B,CAAA;SAAC;SACjE,EAAU,EAAK;SACjB;UAVF,EAUE,CACT;OACE,CAAA;MAEP,EAAW,WACV,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAW,iBAAiB,QAAQ,EAAW,MAAM,UAAU;OAC9D,CAAA;MAEI;QAEZ,kBAAC,GAAD;KAAW,WAAU;KAAQ,WAAU;eAAvC,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA+C;MAAY,CAAA,EACzE,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAsB;SAAW,CAAA,EAC/C,kBAAC,MAAD;SAAI,WAAU;mBAAd,CAAkC,KAAE,EAAM,OAAY;WAClD;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAsB;SAAa,CAAA,EACjD,kBAAC,MAAD;SAAI,WAAU;mBAAqB,EAAM,aAAa,QAAQ;SAAU,CAAA,CACpE;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAsB;SAAY,CAAA,EAChD,kBAAC,MAAD;SAAI,WAAU;mBAAqB,EAAW,EAAM,WAAW;UAAE,MAAM,KAAA;UAAW,QAAQ,KAAA;UAAW,CAAC;SAAM,CAAA,CACxG;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBAAsB;SAAY,CAAA,EAChD,kBAAC,MAAD;SAAI,WAAU;mBAAqB,EAAW,EAAM,WAAW;UAAE,MAAM,KAAA;UAAW,QAAQ,KAAA;UAAW,CAAC;SAAM,CAAA,CACxG;;OACH;QACK;OACR;MACF;;EACK,CAAA"}
|
|
1
|
+
{"version":3,"file":"BrandDetailPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/brands/BrandDetailPage.tsx"],"sourcesContent":["/**\n * Brand Detail Page\n * @module coolandlovely/pages/brands\n *\n * Renders a single brand storefront (`cooBrand`) with its publish-lifecycle\n * transitions (`cooTransitionBrand`), collections and style items.\n */\n\nimport { useParams } from 'react-router-dom';\nimport { Store, Star, ExternalLink, Pencil, ArrowLeft, Shapes, Shirt, Loader2 } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useBrand, useTransitionBrand } from '../../hooks';\nimport { categoryIcon } from '../../utils/icons';\nimport { formatPrice, formatDate, titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooBrandStatus } from '../../types';\n\n/** Allowed next states per the svc brand state machine (UX guidance only;\n * the service is the authority and rejects illegal jumps). */\nconst NEXT_STATES: Record<CooBrandStatus, CooBrandStatus[]> = {\n DRAFT: ['PENDING_REVIEW', 'ARCHIVED'],\n PENDING_REVIEW: ['PUBLISHED', 'DRAFT', 'ARCHIVED'],\n PUBLISHED: ['SUSPENDED', 'ARCHIVED'],\n SUSPENDED: ['PUBLISHED', 'ARCHIVED'],\n ARCHIVED: [],\n};\n\nexport function BrandDetailPage() {\n const { brandId } = useParams<{ brandId: string }>();\n const { navigateTo } = useCoolAndLovely();\n const query = useBrand(brandId);\n const transition = useTransitionBrand();\n\n if (query.isLoading) {\n return (\n <PageLayout title=\"Brand\">\n <LoadingView label=\"Loading brand…\" />\n </PageLayout>\n );\n }\n\n if (query.isError || !query.data) {\n return (\n <PageLayout title=\"Brand\">\n <ErrorView\n title=\"Brand not found\"\n message=\"This storefront could not be loaded or does not exist.\"\n onRetry={() => void query.refetch()}\n />\n </PageLayout>\n );\n }\n\n const brand = query.data;\n const collections = brand.collections ?? [];\n const items = brand.items ?? [];\n const nextStates = NEXT_STATES[brand.status];\n\n return (\n <PageLayout\n title={brand.name}\n description={`@${brand.handle}`}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back\n </button>\n <button\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}/edit`)}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Pencil className=\"h-4 w-4\" /> Edit\n </button>\n </div>\n }\n >\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n {/* Main column */}\n <div className=\"lg:col-span-2\">\n <GlassCard treatment=\"glass\" className=\"overflow-hidden p-0\">\n <Thumbnail\n icon={<Store className=\"h-12 w-12\" />}\n seed={brand.id}\n className=\"h-44 w-full\"\n />\n <div className=\"p-6\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-xl font-semibold text-text-primary\">{brand.name}</h2>\n {brand.isFeatured && (\n <Star className=\"h-4 w-4 fill-status-warning-text text-status-warning-text\" />\n )}\n <StatusBadge status={brand.status} className=\"ml-auto\" />\n </div>\n {brand.tagline && <p className=\"mt-2 text-sm text-text-primary\">{brand.tagline}</p>}\n {brand.bio && <p className=\"mt-3 text-sm text-text-secondary\">{brand.bio}</p>}\n\n {brand.aesthetics.length > 0 && (\n <div className=\"mt-4\">\n <p className=\"mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Aesthetics\n </p>\n <TagList tags={brand.aesthetics.map(titleCase)} tone=\"accent\" max={12} />\n </div>\n )}\n {brand.tags.length > 0 && (\n <div className=\"mt-3\">\n <p className=\"mb-1.5 text-xs font-medium uppercase tracking-wide text-text-secondary\">\n Tags\n </p>\n <TagList tags={brand.tags} max={12} />\n </div>\n )}\n {brand.websiteUrl && (\n <a\n href={brand.websiteUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"mt-4 inline-flex items-center gap-1.5 text-sm font-medium text-primary hover:underline\"\n >\n <ExternalLink className=\"h-4 w-4\" /> {brand.websiteUrl}\n </a>\n )}\n </div>\n </GlassCard>\n\n {/* Collections */}\n <section className=\"mt-6\">\n <div className=\"mb-3 flex items-center justify-between\">\n <h3 className=\"flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Shapes className=\"h-5 w-5\" /> Collections ({collections.length})\n </h3>\n </div>\n {collections.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n No collections yet.\n </p>\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {collections.map((c) => (\n <button\n key={c.id}\n type=\"button\"\n onClick={() => navigateTo(`/collections/${c.id}`)}\n className=\"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-surface p-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <Thumbnail\n icon={<Shapes className=\"h-5 w-5\" />}\n seed={c.id}\n className=\"h-11 w-11 shrink-0\"\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{c.name}</p>\n <p className=\"text-xs text-text-secondary\">{titleCase(c.season)}</p>\n </div>\n <StatusBadge status={c.status} />\n </button>\n ))}\n </div>\n )}\n </section>\n\n {/* Items */}\n <section className=\"mt-6\">\n <h3 className=\"mb-3 flex items-center gap-2 text-lg font-semibold text-text-primary\">\n <Shirt className=\"h-5 w-5\" /> Style items ({items.length})\n </h3>\n {items.length === 0 ? (\n <p className=\"rounded-lg border border-border-seam bg-bg-surface p-4 text-sm text-text-secondary\">\n No items yet.\n </p>\n ) : (\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3\">\n {items.map((item) => (\n <button\n key={item.id}\n type=\"button\"\n onClick={() => navigateTo(`/items/${item.id}`)}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={categoryIcon(item.category, 24)}\n seed={item.id}\n className=\"h-24 w-full\"\n />\n <div className=\"p-3\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{item.name}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">\n {formatPrice(item.price, item.currency)}\n </p>\n </div>\n </button>\n ))}\n </div>\n )}\n </section>\n </div>\n\n {/* Sidebar */}\n <div className=\"space-y-4\">\n <GlassCard treatment=\"insight\" className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Lifecycle</h3>\n <div className=\"mb-3 flex items-center gap-2\">\n <span className=\"text-sm text-text-secondary\">Current:</span>\n <StatusBadge status={brand.status} />\n </div>\n {nextStates.length === 0 ? (\n <p className=\"text-xs text-text-secondary\">\n This brand is archived (terminal state).\n </p>\n ) : (\n <div className=\"flex flex-col gap-2\">\n {nextStates.map((next) => (\n <button\n key={next}\n type=\"button\"\n disabled={transition.isPending}\n onClick={() => transition.mutate({ id: brand.id, status: next })}\n className={cn(\n 'inline-flex items-center justify-center gap-2 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:opacity-50'\n )}\n >\n {transition.isPending && <Loader2 className=\"h-3.5 w-3.5 animate-spin\" />}\n Move to {titleCase(next)}\n </button>\n ))}\n </div>\n )}\n {transition.isError && (\n <p className=\"mt-2 text-xs text-status-error-text\">\n {transition.error instanceof Error ? transition.error.message : 'Transition failed'}\n </p>\n )}\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"mb-3 text-sm font-semibold text-text-primary\">Details</h3>\n <dl className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Handle</dt>\n <dd className=\"text-text-primary\">@{brand.handle}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Featured</dt>\n <dd className=\"text-text-primary\">{brand.isFeatured ? 'Yes' : 'No'}</dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Created</dt>\n <dd className=\"text-text-primary\">\n {formatDate(brand.createdAt, { hour: undefined, minute: undefined })}\n </dd>\n </div>\n <div className=\"flex justify-between gap-2\">\n <dt className=\"text-text-secondary\">Updated</dt>\n <dd className=\"text-text-primary\">\n {formatDate(brand.updatedAt, { hour: undefined, minute: undefined })}\n </dd>\n </div>\n </dl>\n </GlassCard>\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -98,7 +98,7 @@ function x() {
|
|
|
98
98
|
children: "Storefront details"
|
|
99
99
|
}), /* @__PURE__ */ _("p", {
|
|
100
100
|
className: "text-xs text-text-secondary",
|
|
101
|
-
children: "
|
|
101
|
+
children: "Name your storefront and tell shoppers what your brand stands for."
|
|
102
102
|
})] })]
|
|
103
103
|
}),
|
|
104
104
|
/* @__PURE__ */ v("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrandFormPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/brands/BrandFormPage.tsx"],"sourcesContent":["/**\n * Brand Create / Edit Page\n * @module coolandlovely/pages/brands\n *\n * Drives `cooCreateBrand` (new) and `cooUpdateBrand` (edit). The route param\n * `brandId` distinguishes the two modes.\n */\n\nimport { useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Loader2, Save, Store } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { TextField, TextAreaField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useBrand, useCreateBrand, useUpdateBrand } from '../../hooks';\nimport { parseCsv } from '../../utils/formatters';\n\ninterface FormState {\n name: string;\n handle: string;\n tagline: string;\n bio: string;\n aesthetics: string;\n tags: string;\n websiteUrl: string;\n}\n\nconst EMPTY: FormState = {\n name: '',\n handle: '',\n tagline: '',\n bio: '',\n aesthetics: '',\n tags: '',\n websiteUrl: '',\n};\n\nexport function BrandFormPage() {\n const { brandId } = useParams<{ brandId: string }>();\n const isEdit = Boolean(brandId);\n const { navigateTo } = useCoolAndLovely();\n\n const existing = useBrand(isEdit ? brandId : undefined);\n const createBrand = useCreateBrand();\n const updateBrand = useUpdateBrand();\n\n const [form, setForm] = useState<FormState>(EMPTY);\n const [errors, setErrors] = useState<Partial<Record<keyof FormState, string>>>({});\n\n useEffect(() => {\n if (isEdit && existing.data) {\n const b = existing.data;\n setForm({\n name: b.name,\n handle: b.handle,\n tagline: b.tagline ?? '',\n bio: b.bio ?? '',\n aesthetics: b.aesthetics.join(', '),\n tags: b.tags.join(', '),\n websiteUrl: b.websiteUrl ?? '',\n });\n }\n }, [isEdit, existing.data]);\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) =>\n setForm((f) => ({ ...f, [key]: value }));\n\n const mutation = isEdit ? updateBrand : createBrand;\n\n const validate = (): boolean => {\n const next: Partial<Record<keyof FormState, string>> = {};\n if (!form.name.trim()) next.name = 'Name is required';\n if (!isEdit && !form.handle.trim()) next.handle = 'Handle is required';\n setErrors(next);\n return Object.keys(next).length === 0;\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!validate()) return;\n\n if (isEdit && brandId) {\n updateBrand.mutate(\n {\n id: brandId,\n input: {\n name: form.name.trim(),\n tagline: form.tagline.trim() || undefined,\n bio: form.bio.trim() || undefined,\n aesthetics: parseCsv(form.aesthetics),\n tags: parseCsv(form.tags),\n websiteUrl: form.websiteUrl.trim() || undefined,\n },\n },\n { onSuccess: () => navigateTo(`/brands/${brandId}`) }\n );\n } else {\n createBrand.mutate(\n {\n name: form.name.trim(),\n handle: form.handle.trim(),\n tagline: form.tagline.trim() || undefined,\n bio: form.bio.trim() || undefined,\n aesthetics: parseCsv(form.aesthetics),\n tags: parseCsv(form.tags),\n websiteUrl: form.websiteUrl.trim() || undefined,\n },\n { onSuccess: (brand) => navigateTo(`/brands/${brand.id}`) }\n );\n }\n };\n\n if (isEdit && existing.isLoading) {\n return (\n <PageLayout title=\"Edit brand\">\n <LoadingView label=\"Loading brand…\" />\n </PageLayout>\n );\n }\n\n if (isEdit && (existing.isError || !existing.data)) {\n return (\n <PageLayout title=\"Edit brand\">\n <ErrorView title=\"Brand not found\" onRetry={() => void existing.refetch()} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={isEdit ? 'Edit brand' : 'New brand'}\n description={isEdit ? 'Update this storefront' : 'Launch a new storefront on the platform'}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo(isEdit && brandId ? `/brands/${brandId}` : '/brands')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <Store className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Storefront details</h2>\n <p className=\"text-xs text-text-secondary\">\n Imagery (logo/cover) is attached via the workspace files service.\n </p>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n id=\"name\"\n label=\"Brand name\"\n value={form.name}\n onChange={(v) => setField('name', v)}\n placeholder=\"Aurora Atelier\"\n required\n error={errors.name}\n />\n <TextField\n id=\"handle\"\n label=\"Handle\"\n value={form.handle}\n onChange={(v) => setField('handle', v)}\n placeholder=\"aurora-atelier\"\n required={!isEdit}\n hint={isEdit ? 'Handle cannot be changed after creation' : 'Unique per workspace'}\n error={errors.handle}\n />\n </div>\n\n <TextField\n id=\"tagline\"\n label=\"Tagline\"\n value={form.tagline}\n onChange={(v) => setField('tagline', v)}\n placeholder=\"Quiet luxury, loud ideas\"\n className=\"mt-4\"\n />\n\n <TextAreaField\n id=\"bio\"\n label=\"Bio\"\n value={form.bio}\n onChange={(v) => setField('bio', v)}\n placeholder=\"Tell shoppers what your brand stands for…\"\n rows={4}\n className=\"mt-4\"\n />\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n id=\"aesthetics\"\n label=\"Aesthetics\"\n value={form.aesthetics}\n onChange={(v) => setField('aesthetics', v)}\n placeholder=\"minimal, Y2K, coastal\"\n hint=\"Comma-separated descriptors\"\n />\n <TextField\n id=\"tags\"\n label=\"Tags\"\n value={form.tags}\n onChange={(v) => setField('tags', v)}\n placeholder=\"sustainable, handmade\"\n hint=\"Comma-separated\"\n />\n </div>\n\n <TextField\n id=\"websiteUrl\"\n label=\"Website\"\n type=\"url\"\n value={form.websiteUrl}\n onChange={(v) => setField('websiteUrl', v)}\n placeholder=\"https://example.com\"\n className=\"mt-4\"\n />\n\n {mutation.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {mutation.error instanceof Error ? mutation.error.message : 'Something went wrong'}\n </p>\n )}\n\n <div className=\"mt-6 flex justify-end gap-2\">\n <button\n type=\"submit\"\n disabled={mutation.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {mutation.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit ? 'Save changes' : 'Create brand'}\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BrandFormPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/brands/BrandFormPage.tsx"],"sourcesContent":["/**\n * Brand Create / Edit Page\n * @module coolandlovely/pages/brands\n *\n * Drives `cooCreateBrand` (new) and `cooUpdateBrand` (edit). The route param\n * `brandId` distinguishes the two modes.\n */\n\nimport { useEffect, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { ArrowLeft, Loader2, Save, Store } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout } from '../../components/PageLayout';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { TextField, TextAreaField } from '../../components/Field';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useBrand, useCreateBrand, useUpdateBrand } from '../../hooks';\nimport { parseCsv } from '../../utils/formatters';\n\ninterface FormState {\n name: string;\n handle: string;\n tagline: string;\n bio: string;\n aesthetics: string;\n tags: string;\n websiteUrl: string;\n}\n\nconst EMPTY: FormState = {\n name: '',\n handle: '',\n tagline: '',\n bio: '',\n aesthetics: '',\n tags: '',\n websiteUrl: '',\n};\n\nexport function BrandFormPage() {\n const { brandId } = useParams<{ brandId: string }>();\n const isEdit = Boolean(brandId);\n const { navigateTo } = useCoolAndLovely();\n\n const existing = useBrand(isEdit ? brandId : undefined);\n const createBrand = useCreateBrand();\n const updateBrand = useUpdateBrand();\n\n const [form, setForm] = useState<FormState>(EMPTY);\n const [errors, setErrors] = useState<Partial<Record<keyof FormState, string>>>({});\n\n useEffect(() => {\n if (isEdit && existing.data) {\n const b = existing.data;\n setForm({\n name: b.name,\n handle: b.handle,\n tagline: b.tagline ?? '',\n bio: b.bio ?? '',\n aesthetics: b.aesthetics.join(', '),\n tags: b.tags.join(', '),\n websiteUrl: b.websiteUrl ?? '',\n });\n }\n }, [isEdit, existing.data]);\n\n const setField = <K extends keyof FormState>(key: K, value: FormState[K]) =>\n setForm((f) => ({ ...f, [key]: value }));\n\n const mutation = isEdit ? updateBrand : createBrand;\n\n const validate = (): boolean => {\n const next: Partial<Record<keyof FormState, string>> = {};\n if (!form.name.trim()) next.name = 'Name is required';\n if (!isEdit && !form.handle.trim()) next.handle = 'Handle is required';\n setErrors(next);\n return Object.keys(next).length === 0;\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!validate()) return;\n\n if (isEdit && brandId) {\n updateBrand.mutate(\n {\n id: brandId,\n input: {\n name: form.name.trim(),\n tagline: form.tagline.trim() || undefined,\n bio: form.bio.trim() || undefined,\n aesthetics: parseCsv(form.aesthetics),\n tags: parseCsv(form.tags),\n websiteUrl: form.websiteUrl.trim() || undefined,\n },\n },\n { onSuccess: () => navigateTo(`/brands/${brandId}`) }\n );\n } else {\n createBrand.mutate(\n {\n name: form.name.trim(),\n handle: form.handle.trim(),\n tagline: form.tagline.trim() || undefined,\n bio: form.bio.trim() || undefined,\n aesthetics: parseCsv(form.aesthetics),\n tags: parseCsv(form.tags),\n websiteUrl: form.websiteUrl.trim() || undefined,\n },\n { onSuccess: (brand) => navigateTo(`/brands/${brand.id}`) }\n );\n }\n };\n\n if (isEdit && existing.isLoading) {\n return (\n <PageLayout title=\"Edit brand\">\n <LoadingView label=\"Loading brand…\" />\n </PageLayout>\n );\n }\n\n if (isEdit && (existing.isError || !existing.data)) {\n return (\n <PageLayout title=\"Edit brand\">\n <ErrorView title=\"Brand not found\" onRetry={() => void existing.refetch()} />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout\n title={isEdit ? 'Edit brand' : 'New brand'}\n description={isEdit ? 'Update this storefront' : 'Launch a new storefront on the platform'}\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo(isEdit && brandId ? `/brands/${brandId}` : '/brands')}\n className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam px-3 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Cancel\n </button>\n }\n >\n <form onSubmit={handleSubmit} className=\"mx-auto max-w-2xl\">\n <GlassCard treatment=\"plain\" className=\"p-6\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-11 w-11 items-center justify-center rounded-lg bg-accent-purple-subtle text-text-primary\">\n <Store className=\"h-5 w-5\" />\n </div>\n <div>\n <h2 className=\"text-base font-semibold text-text-primary\">Storefront details</h2>\n <p className=\"text-xs text-text-secondary\">\n Name your storefront and tell shoppers what your brand stands for.\n </p>\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n id=\"name\"\n label=\"Brand name\"\n value={form.name}\n onChange={(v) => setField('name', v)}\n placeholder=\"Aurora Atelier\"\n required\n error={errors.name}\n />\n <TextField\n id=\"handle\"\n label=\"Handle\"\n value={form.handle}\n onChange={(v) => setField('handle', v)}\n placeholder=\"aurora-atelier\"\n required={!isEdit}\n hint={isEdit ? 'Handle cannot be changed after creation' : 'Unique per workspace'}\n error={errors.handle}\n />\n </div>\n\n <TextField\n id=\"tagline\"\n label=\"Tagline\"\n value={form.tagline}\n onChange={(v) => setField('tagline', v)}\n placeholder=\"Quiet luxury, loud ideas\"\n className=\"mt-4\"\n />\n\n <TextAreaField\n id=\"bio\"\n label=\"Bio\"\n value={form.bio}\n onChange={(v) => setField('bio', v)}\n placeholder=\"Tell shoppers what your brand stands for…\"\n rows={4}\n className=\"mt-4\"\n />\n\n <div className=\"mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <TextField\n id=\"aesthetics\"\n label=\"Aesthetics\"\n value={form.aesthetics}\n onChange={(v) => setField('aesthetics', v)}\n placeholder=\"minimal, Y2K, coastal\"\n hint=\"Comma-separated descriptors\"\n />\n <TextField\n id=\"tags\"\n label=\"Tags\"\n value={form.tags}\n onChange={(v) => setField('tags', v)}\n placeholder=\"sustainable, handmade\"\n hint=\"Comma-separated\"\n />\n </div>\n\n <TextField\n id=\"websiteUrl\"\n label=\"Website\"\n type=\"url\"\n value={form.websiteUrl}\n onChange={(v) => setField('websiteUrl', v)}\n placeholder=\"https://example.com\"\n className=\"mt-4\"\n />\n\n {mutation.isError && (\n <p className=\"mt-4 rounded-lg bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {mutation.error instanceof Error ? mutation.error.message : 'Something went wrong'}\n </p>\n )}\n\n <div className=\"mt-6 flex justify-end gap-2\">\n <button\n type=\"submit\"\n disabled={mutation.isPending}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-5 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90 disabled:opacity-50\"\n >\n {mutation.isPending ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n {isEdit ? 'Save changes' : 'Create brand'}\n </button>\n </div>\n </GlassCard>\n </form>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -72,7 +72,11 @@ function S() {
|
|
|
72
72
|
E((e) => !e), O(0);
|
|
73
73
|
},
|
|
74
74
|
className: t("ml-auto inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors", T ? "bg-status-warning-bg-subtle text-status-warning-text" : "bg-bg-sunken text-text-secondary hover:text-text-primary"),
|
|
75
|
-
children: [
|
|
75
|
+
children: [
|
|
76
|
+
/* @__PURE__ */ v(h, { className: t("h-3.5 w-3.5", T && "fill-status-warning-text") }),
|
|
77
|
+
" ",
|
|
78
|
+
"Featured"
|
|
79
|
+
]
|
|
76
80
|
})]
|
|
77
81
|
}), k.isLoading ? /* @__PURE__ */ v(a, { label: "Loading brands…" }) : k.isError ? /* @__PURE__ */ v(i, { onRetry: () => void k.refetch() }) : A.length === 0 ? /* @__PURE__ */ v(u, {
|
|
78
82
|
illustration: "empty-data",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrandsListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/brands/BrandsListPage.tsx"],"sourcesContent":["/**\n * Brands List Page\n * @module coolandlovely/pages/brands\n *\n * Lists brand storefronts (cursor-paginated `cooListBrands`) with status &\n * featured filters. Each card links to the brand detail page.\n */\n\nimport { useState } from 'react';\nimport { Store, Plus, Star, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useBrands } from '../../hooks';\nimport { titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooBrandStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst STATUS_OPTIONS: Array<{ value: CooBrandStatus | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PENDING_REVIEW', label: 'In review' },\n { value: 'SUSPENDED', label: 'Suspended' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nexport function BrandsListPage() {\n const { navigateTo } = useCoolAndLovely();\n const [status, setStatus] = useState<CooBrandStatus | 'ALL'>('ALL');\n const [featuredOnly, setFeaturedOnly] = useState(false);\n const [offset, setOffset] = useState(0);\n\n const query = useBrands({\n status: status === 'ALL' ? undefined : status,\n featured: featuredOnly ? true : undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n const brands = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title=\"Brands\"\n description=\"Storefronts on the platform with a real publish lifecycle\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" /> New brand\n </button>\n }\n >\n {/* Filters */}\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n {STATUS_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => {\n setStatus(opt.value);\n setOffset(0);\n }}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n status === opt.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {opt.label}\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => {\n setFeaturedOnly((v) => !v);\n setOffset(0);\n }}\n className={cn(\n 'ml-auto inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n featuredOnly\n ? 'bg-status-warning-bg-subtle text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n <Star className={cn('h-3.5 w-3.5', featuredOnly && 'fill-status-warning-text')} /> Featured\n </button>\n </div>\n\n {query.isLoading ? (\n <LoadingView label=\"Loading brands…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : brands.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Store className=\"h-10 w-10\" />}\n title=\"No brands yet\"\n description=\"Launch the first storefront to start building the catalogue.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <Plus className=\"h-4 w-4\" /> New brand\n </button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {brands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-9 w-9\" />}\n seed={brand.id}\n className=\"h-32 w-full\"\n />\n <div className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {brand.name}\n </h3>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </div>\n {brand.tagline && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">{brand.tagline}</p>\n )}\n {brand.aesthetics.length > 0 && (\n <TagList tags={brand.aesthetics.map(titleCase)} max={4} className=\"mt-3\" tone=\"accent\" />\n )}\n </div>\n </button>\n ))}\n </div>\n\n {/* Pagination */}\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-center gap-2\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Previous\n </button>\n <span className=\"text-sm text-text-secondary\">Page {offset / PAGE_SIZE + 1}</span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"BrandsListPage.js","names":[],"sources":["../../../../src/coolandlovely/pages/brands/BrandsListPage.tsx"],"sourcesContent":["/**\n * Brands List Page\n * @module coolandlovely/pages/brands\n *\n * Lists brand storefronts (cursor-paginated `cooListBrands`) with status &\n * featured filters. Each card links to the brand detail page.\n */\n\nimport { useState } from 'react';\nimport { Store, Plus, Star, ChevronLeft, ChevronRight } from 'lucide-react';\nimport { PageLayout } from '../../components/PageLayout';\nimport { StatusBadge } from '../../components/StatusBadge';\nimport { EmptyState } from '../../components/EmptyState';\nimport { LoadingView, ErrorView } from '../../components/StateViews';\nimport { Thumbnail } from '../../components/Thumbnail';\nimport { TagList } from '../../components/TagList';\nimport { useCoolAndLovely } from '../../context/CoolAndLovelyContext';\nimport { useBrands } from '../../hooks';\nimport { titleCase } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { CooBrandStatus } from '../../types';\n\nconst PAGE_SIZE = 24;\n\nconst STATUS_OPTIONS: Array<{ value: CooBrandStatus | 'ALL'; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'PUBLISHED', label: 'Published' },\n { value: 'DRAFT', label: 'Draft' },\n { value: 'PENDING_REVIEW', label: 'In review' },\n { value: 'SUSPENDED', label: 'Suspended' },\n { value: 'ARCHIVED', label: 'Archived' },\n];\n\nexport function BrandsListPage() {\n const { navigateTo } = useCoolAndLovely();\n const [status, setStatus] = useState<CooBrandStatus | 'ALL'>('ALL');\n const [featuredOnly, setFeaturedOnly] = useState(false);\n const [offset, setOffset] = useState(0);\n\n const query = useBrands({\n status: status === 'ALL' ? undefined : status,\n featured: featuredOnly ? true : undefined,\n pagination: { limit: PAGE_SIZE, offset },\n });\n\n const brands = query.data?.items ?? [];\n const hasMore = query.data?.pageInfo.hasMore ?? false;\n\n return (\n <PageLayout\n title=\"Brands\"\n description=\"Storefronts on the platform with a real publish lifecycle\"\n actions={\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text transition-colors hover:bg-action-primary-bg/90\"\n >\n <Plus className=\"h-4 w-4\" /> New brand\n </button>\n }\n >\n {/* Filters */}\n <div className=\"mb-6 flex flex-wrap items-center gap-2\">\n {STATUS_OPTIONS.map((opt) => (\n <button\n key={opt.value}\n type=\"button\"\n onClick={() => {\n setStatus(opt.value);\n setOffset(0);\n }}\n className={cn(\n 'rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n status === opt.value\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n {opt.label}\n </button>\n ))}\n <button\n type=\"button\"\n onClick={() => {\n setFeaturedOnly((v) => !v);\n setOffset(0);\n }}\n className={cn(\n 'ml-auto inline-flex items-center gap-1.5 rounded-full px-3 py-1.5 text-sm font-medium transition-colors',\n featuredOnly\n ? 'bg-status-warning-bg-subtle text-status-warning-text'\n : 'bg-bg-sunken text-text-secondary hover:text-text-primary'\n )}\n >\n <Star className={cn('h-3.5 w-3.5', featuredOnly && 'fill-status-warning-text')} />{' '}\n Featured\n </button>\n </div>\n\n {query.isLoading ? (\n <LoadingView label=\"Loading brands…\" />\n ) : query.isError ? (\n <ErrorView onRetry={() => void query.refetch()} />\n ) : brands.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<Store className=\"h-10 w-10\" />}\n title=\"No brands yet\"\n description=\"Launch the first storefront to start building the catalogue.\"\n action={\n <button\n type=\"button\"\n onClick={() => navigateTo('/brands/new')}\n className=\"inline-flex items-center gap-1.5 rounded-lg bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text\"\n >\n <Plus className=\"h-4 w-4\" /> New brand\n </button>\n }\n />\n ) : (\n <>\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {brands.map((brand) => (\n <button\n key={brand.id}\n type=\"button\"\n onClick={() => navigateTo(`/brands/${brand.id}`)}\n className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface text-left shadow-sm transition-all hover:border-accent-foreground/20 hover:shadow-md\"\n >\n <Thumbnail\n icon={<Store className=\"h-9 w-9\" />}\n seed={brand.id}\n className=\"h-32 w-full\"\n />\n <div className=\"p-4\">\n <div className=\"flex items-start justify-between gap-2\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-1.5\">\n <h3 className=\"truncate text-base font-semibold text-text-primary\">\n {brand.name}\n </h3>\n {brand.isFeatured && (\n <Star className=\"h-3.5 w-3.5 shrink-0 fill-status-warning-text text-status-warning-text\" />\n )}\n </div>\n <p className=\"truncate text-xs text-text-secondary\">@{brand.handle}</p>\n </div>\n <StatusBadge status={brand.status} />\n </div>\n {brand.tagline && (\n <p className=\"mt-2 line-clamp-2 text-sm text-text-secondary\">{brand.tagline}</p>\n )}\n {brand.aesthetics.length > 0 && (\n <TagList\n tags={brand.aesthetics.map(titleCase)}\n max={4}\n className=\"mt-3\"\n tone=\"accent\"\n />\n )}\n </div>\n </button>\n ))}\n </div>\n\n {/* Pagination */}\n {(offset > 0 || hasMore) && (\n <div className=\"mt-6 flex items-center justify-center gap-2\">\n <button\n type=\"button\"\n disabled={offset === 0}\n onClick={() => setOffset((o) => Math.max(0, o - PAGE_SIZE))}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Previous\n </button>\n <span className=\"text-sm text-text-secondary\">Page {offset / PAGE_SIZE + 1}</span>\n <button\n type=\"button\"\n disabled={!hasMore}\n onClick={() => setOffset((o) => o + PAGE_SIZE)}\n className=\"inline-flex items-center gap-1 rounded-lg border border-border-seam px-3 py-1.5 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken disabled:cursor-not-allowed disabled:opacity-40\"\n >\n Next <ChevronRight className=\"h-4 w-4\" />\n </button>\n </div>\n )}\n </>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|