negodesign 0.0.30 → 0.0.32

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.
Files changed (102) hide show
  1. package/dist/components/NegoDesign.svelte +2 -10
  2. package/dist/components/NegoDesign.svelte.d.ts +2 -7
  3. package/dist/components/core/banner/CtaCardBanner.svelte +1 -1
  4. package/dist/components/core/banner/types.d.ts +16 -6
  5. package/dist/components/core/card/types.d.ts +94 -0
  6. package/dist/components/core/carousel/grid/media/types.d.ts +11 -1
  7. package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +2 -2
  8. package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte.d.ts +2 -2
  9. package/dist/components/core/carousel/grid/profile/types.d.ts +11 -1
  10. package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +2 -2
  11. package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte.d.ts +2 -2
  12. package/dist/components/core/carousel/highlights/types.d.ts +11 -0
  13. package/dist/components/core/carousel/promotion/types.d.ts +10 -0
  14. package/dist/components/core/carousel/types.d.ts +56 -0
  15. package/dist/components/core/datatable/data/data-table.d.ts +25 -0
  16. package/dist/components/core/footer/types.d.ts +28 -0
  17. package/dist/components/core/nav/data/nav-menu-sidebar.d.ts +29 -0
  18. package/dist/components/core/nav/data/nav-menu.d.ts +43 -0
  19. package/dist/components/core/nav/data/nav-user-sidebar.d.ts +14 -0
  20. package/dist/components/core/nav/ui/menu-links.svelte +1 -1
  21. package/dist/components/core/panel/CarouselSlot.svelte +1 -1
  22. package/dist/components/core/panel/type.d.ts +11 -0
  23. package/dist/components/core/search/types.d.ts +17 -5
  24. package/dist/components/core/sidebar/types.d.ts +19 -0
  25. package/dist/components/core/sidebar/types.js +1 -0
  26. package/dist/components/core/tabs/data/TabModel.d.ts +11 -0
  27. package/dist/components/core/tabs/types.d.ts +11 -0
  28. package/dist/components/core/tabs/types.js +1 -0
  29. package/dist/components/pages/admin/01/ui/types.d.ts +21 -0
  30. package/dist/components/pages/admin/01/ui/types.js +1 -0
  31. package/dist/components/pages/product-details/01/ProductDetails01.svelte +71 -0
  32. package/dist/components/pages/product-details/01/ProductDetails01.svelte.d.ts +11 -0
  33. package/dist/components/pages/product-details/02/ProductDetails02.svelte +88 -0
  34. package/dist/components/pages/product-details/02/ProductDetails02.svelte.d.ts +11 -0
  35. package/dist/components/pages/product-details/03/ProductDetails03.svelte +77 -0
  36. package/dist/components/pages/product-details/03/ProductDetails03.svelte.d.ts +11 -0
  37. package/dist/components/pages/product-details/04/ProductDetails04.svelte +65 -0
  38. package/dist/components/pages/product-details/04/ProductDetails04.svelte.d.ts +11 -0
  39. package/dist/components/pages/product-details/ProductDetails.svelte +38 -0
  40. package/dist/components/pages/product-details/ProductDetails.svelte.d.ts +4 -0
  41. package/dist/components/pages/product-details/shared/ProductDetailActions.svelte +82 -0
  42. package/dist/components/pages/product-details/shared/ProductDetailActions.svelte.d.ts +13 -0
  43. package/dist/components/pages/product-details/shared/ProductDetailBreadcrumb.svelte +46 -0
  44. package/dist/components/pages/product-details/shared/ProductDetailBreadcrumb.svelte.d.ts +8 -0
  45. package/dist/components/pages/product-details/shared/ProductDetailDescription.svelte +38 -0
  46. package/dist/components/pages/product-details/shared/ProductDetailDescription.svelte.d.ts +8 -0
  47. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte +40 -0
  48. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte.d.ts +7 -0
  49. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte +72 -0
  50. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte.d.ts +9 -0
  51. package/dist/components/pages/product-details/shared/ProductDetailHeader.svelte +41 -0
  52. package/dist/components/pages/product-details/shared/ProductDetailHeader.svelte.d.ts +9 -0
  53. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte +70 -0
  54. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte.d.ts +9 -0
  55. package/dist/components/pages/product-details/shared/ProductDetailMap.svelte +57 -0
  56. package/dist/components/pages/product-details/shared/ProductDetailMap.svelte.d.ts +9 -0
  57. package/dist/components/pages/product-details/shared/ProductDetailPrice.svelte +44 -0
  58. package/dist/components/pages/product-details/shared/ProductDetailPrice.svelte.d.ts +9 -0
  59. package/dist/components/pages/product-details/shared/ProductDetailRating.svelte +42 -0
  60. package/dist/components/pages/product-details/shared/ProductDetailRating.svelte.d.ts +8 -0
  61. package/dist/components/pages/product-details/shared/ProductDetailReviews.svelte +70 -0
  62. package/dist/components/pages/product-details/shared/ProductDetailReviews.svelte.d.ts +8 -0
  63. package/dist/components/pages/product-details/shared/ProductDetailTabs.svelte +96 -0
  64. package/dist/components/pages/product-details/shared/ProductDetailTabs.svelte.d.ts +12 -0
  65. package/dist/components/pages/product-details/shared/ProductDetailTags.svelte +35 -0
  66. package/dist/components/pages/product-details/shared/ProductDetailTags.svelte.d.ts +8 -0
  67. package/dist/components/pages/product-details/shared/ProductPurchasePanel.svelte +58 -0
  68. package/dist/components/pages/product-details/shared/ProductPurchasePanel.svelte.d.ts +11 -0
  69. package/dist/components/pages/product-details/types.d.ts +170 -0
  70. package/dist/components/pages/product-details/types.js +1 -0
  71. package/dist/components/pages/profile-user/01/ProfileUser01.svelte +113 -0
  72. package/dist/components/pages/profile-user/01/ProfileUser01.svelte.d.ts +27 -0
  73. package/dist/components/pages/profile-user/02/ProfileUser02.svelte +83 -0
  74. package/dist/components/pages/profile-user/02/ProfileUser02.svelte.d.ts +20 -0
  75. package/dist/components/pages/profile-user/03/ProfileUser03.svelte +85 -0
  76. package/dist/components/pages/profile-user/03/ProfileUser03.svelte.d.ts +20 -0
  77. package/dist/components/pages/profile-user/ProfileUser.svelte +53 -0
  78. package/dist/components/pages/profile-user/ProfileUser.svelte.d.ts +4 -0
  79. package/dist/components/pages/profile-user/shared/ProfileUserAddress.svelte +84 -0
  80. package/dist/components/pages/profile-user/shared/ProfileUserAddress.svelte.d.ts +8 -0
  81. package/dist/components/pages/profile-user/shared/ProfileUserHeader.svelte +104 -0
  82. package/dist/components/pages/profile-user/shared/ProfileUserHeader.svelte.d.ts +9 -0
  83. package/dist/components/pages/profile-user/shared/ProfileUserResetPassword.svelte +116 -0
  84. package/dist/components/pages/profile-user/shared/ProfileUserResetPassword.svelte.d.ts +7 -0
  85. package/dist/components/pages/profile-user/shared/ProfileUserSetting.svelte +176 -0
  86. package/dist/components/pages/profile-user/shared/ProfileUserSetting.svelte.d.ts +14 -0
  87. package/dist/components/pages/profile-user/shared/ProfileUserTabs.svelte +78 -0
  88. package/dist/components/pages/profile-user/shared/ProfileUserTabs.svelte.d.ts +22 -0
  89. package/dist/components/pages/profile-user/types.d.ts +144 -0
  90. package/dist/components/pages/profile-user/types.js +1 -0
  91. package/dist/components/types.d.ts +6 -0
  92. package/dist/components/types.js +1 -0
  93. package/dist/globals.css +210 -3
  94. package/dist/i18n/langs/en.d.ts +83 -0
  95. package/dist/i18n/langs/en.js +83 -0
  96. package/dist/i18n/langs/pt.d.ts +83 -0
  97. package/dist/i18n/langs/pt.js +83 -0
  98. package/dist/i18n/translations.d.ts +166 -0
  99. package/dist/index.d.ts +190 -25
  100. package/dist/index.js +190 -25
  101. package/dist/types/index.d.ts +159 -5
  102. package/package.json +1 -1
@@ -0,0 +1,42 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Avaliação (estrelas + contagem) do produto.
4
+ * Quando `isLoading` é true, exibe um Skeleton.
5
+ * @component
6
+ * @example
7
+ * ```svelte
8
+ * <ProductDetailRating rating={4.5} reviewsCount={120} />
9
+ * ```
10
+ */
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import CardStarRating from "../../../core/card/shared/CardStarRating.svelte";
15
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
16
+
17
+ let {
18
+ rating,
19
+ reviewsCount,
20
+ isLoading = false,
21
+ }: {
22
+ rating?: number;
23
+ reviewsCount?: number;
24
+ isLoading?: boolean;
25
+ } = $props();
26
+ </script>
27
+
28
+ {#if isLoading}
29
+ <div class="flex items-center gap-2">
30
+ <Skeleton class="h-6 w-24 rounded-md" />
31
+ <Skeleton class="h-4 w-16 rounded-md" />
32
+ </div>
33
+ {:else if rating}
34
+ <div class="flex items-center gap-2">
35
+ <CardStarRating startNumber={rating} startMax={5} />
36
+ {#if reviewsCount}
37
+ <span class="text-sm text-muted-foreground">
38
+ ({reviewsCount} avaliações)
39
+ </span>
40
+ {/if}
41
+ </div>
42
+ {/if}
@@ -0,0 +1,8 @@
1
+ type $$ComponentProps = {
2
+ rating?: number;
3
+ reviewsCount?: number;
4
+ isLoading?: boolean;
5
+ };
6
+ declare const ProductDetailRating: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type ProductDetailRating = ReturnType<typeof ProductDetailRating>;
8
+ export default ProductDetailRating;
@@ -0,0 +1,70 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Lista de avaliações de clientes do produto.
4
+ * Quando `isLoading` é true, exibe Skeletons.
5
+ * @component
6
+ * @example
7
+ * ```svelte
8
+ * <ProductDetailReviews reviews={data.reviews} />
9
+ * ```
10
+ */
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import CardStarRating from "../../../core/card/shared/CardStarRating.svelte";
15
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
16
+ import type { ProductDetailsReview } from "../types";
17
+
18
+ let {
19
+ reviews,
20
+ isLoading = false,
21
+ }: {
22
+ reviews?: ProductDetailsReview[];
23
+ isLoading?: boolean;
24
+ } = $props();
25
+ </script>
26
+
27
+ {#if isLoading}
28
+ <div class="flex flex-col gap-6">
29
+ {#each Array.from({ length: 2 }) as _, i (i)}
30
+ <div class="flex flex-col gap-2 border-b pb-6 last:border-b-0">
31
+ <div class="flex items-center justify-between">
32
+ <div class="flex items-center gap-3">
33
+ <Skeleton class="size-9 rounded-full" />
34
+ <div class="flex flex-col gap-1">
35
+ <Skeleton class="h-4 w-28 rounded-md" />
36
+ <Skeleton class="h-3 w-20 rounded-md" />
37
+ </div>
38
+ </div>
39
+ <Skeleton class="h-5 w-24 rounded-md" />
40
+ </div>
41
+ <Skeleton class="h-4 w-full rounded-md" />
42
+ <Skeleton class="h-4 w-3/4 rounded-md" />
43
+ </div>
44
+ {/each}
45
+ </div>
46
+ {:else if reviews?.length}
47
+ <div class="flex flex-col gap-6">
48
+ {#each reviews as review (review.id)}
49
+ <article class="flex flex-col gap-2 border-b pb-6 last:border-b-0">
50
+ <div class="flex items-center justify-between">
51
+ <div class="flex items-center gap-3">
52
+ <div class="flex size-9 items-center justify-center rounded-full bg-muted text-sm font-semibold">
53
+ {review.author.charAt(0)}
54
+ </div>
55
+ <div>
56
+ <p class="text-sm font-medium">{review.author}</p>
57
+ {#if review.date}
58
+ <p class="text-xs text-muted-foreground">{review.date}</p>
59
+ {/if}
60
+ </div>
61
+ </div>
62
+ <CardStarRating startNumber={review.rating} startMax={5} />
63
+ </div>
64
+ <p class="text-sm leading-6 text-foreground/85">{review.comment}</p>
65
+ </article>
66
+ {/each}
67
+ </div>
68
+ {:else}
69
+ <p class="text-muted-foreground">Sem avaliações disponíveis.</p>
70
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { ProductDetailsReview } from "../types";
2
+ type $$ComponentProps = {
3
+ reviews?: ProductDetailsReview[];
4
+ isLoading?: boolean;
5
+ };
6
+ declare const ProductDetailReviews: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type ProductDetailReviews = ReturnType<typeof ProductDetailReviews>;
8
+ export default ProductDetailReviews;
@@ -0,0 +1,96 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Abas de conteúdo do produto (descrição, avaliações, etc).
4
+ * Quando `isLoading` é true, exibe Skeletons.
5
+ * @component
6
+ * @example
7
+ * ```svelte
8
+ * <ProductDetailTabs tabs={data.tabs} {data}>
9
+ * {#snippet tabContent(tab)}
10
+ * ...
11
+ * {/snippet}
12
+ * </ProductDetailTabs>
13
+ * ```
14
+ */
15
+ </script>
16
+
17
+ <script lang="ts">
18
+ import type { Snippet } from "svelte";
19
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
20
+ import type { ProductDetailsData, ProductDetailsTab } from "../types";
21
+ import ProductDetailDescription from "./ProductDetailDescription.svelte";
22
+ import ProductDetailFeatures from "./ProductDetailFeatures.svelte";
23
+ import ProductDetailReviews from "./ProductDetailReviews.svelte";
24
+
25
+ let {
26
+ data,
27
+ tabs,
28
+ isLoading = false,
29
+ children,
30
+ }: {
31
+ data: ProductDetailsData;
32
+ tabs?: ProductDetailsTab[];
33
+ isLoading?: boolean;
34
+ /** Snippet opcional para renderizar o conteúdo de cada aba. */
35
+ children?: Snippet<[ProductDetailsTab]>;
36
+ } = $props();
37
+
38
+ const defaultTabs = $derived(
39
+ tabs?.length
40
+ ? tabs
41
+ : [
42
+ { id: "description", label: "Descrição" },
43
+ { id: "reviews", label: "Avaliações" },
44
+ ],
45
+ );
46
+
47
+ let activeTab = $state<string | undefined>(undefined);
48
+ const currentTab = $derived(activeTab ?? defaultTabs[0]?.id ?? "description");
49
+ </script>
50
+
51
+ <div>
52
+ <div class="mb-6 flex gap-1 border-b">
53
+ {#if isLoading}
54
+ {#each defaultTabs as tab (tab.id)}
55
+ <Skeleton class="h-9 w-24 rounded-t-md" />
56
+ {/each}
57
+ {:else}
58
+ {#each defaultTabs as tab (tab.id)}
59
+ <button
60
+ type="button"
61
+ onclick={() => (activeTab = tab.id)}
62
+ class="px-4 py-2 text-sm font-medium transition-colors {currentTab ===
63
+ tab.id
64
+ ? 'border-b-2 border-foreground text-foreground'
65
+ : 'border-b-2 border-transparent text-muted-foreground hover:text-foreground'}"
66
+ >
67
+ {tab.label}
68
+ </button>
69
+ {/each}
70
+ {/if}
71
+ </div>
72
+
73
+ {#if isLoading}
74
+ <div class="flex max-w-3xl flex-col gap-2">
75
+ {#each Array.from({ length: 4 }) as _, i (i)}
76
+ <Skeleton class="h-4 w-full rounded-md" />
77
+ {/each}
78
+ </div>
79
+ {:else if children}
80
+ {@render children(defaultTabs.find((tab) => tab.id === currentTab) ?? defaultTabs[0])}
81
+ {:else if currentTab === "description"}
82
+ <div class="flex max-w-3xl flex-col gap-5">
83
+ <ProductDetailDescription
84
+ description={data.description}
85
+ longDescription={data.longDescription}
86
+ />
87
+ {#if data.features}
88
+ <ProductDetailFeatures features={data.features} />
89
+ {/if}
90
+ </div>
91
+ {:else if currentTab === "reviews"}
92
+ <div class="max-w-3xl">
93
+ <ProductDetailReviews reviews={data.reviews} />
94
+ </div>
95
+ {/if}
96
+ </div>
@@ -0,0 +1,12 @@
1
+ import type { Snippet } from "svelte";
2
+ import type { ProductDetailsData, ProductDetailsTab } from "../types";
3
+ type $$ComponentProps = {
4
+ data: ProductDetailsData;
5
+ tabs?: ProductDetailsTab[];
6
+ isLoading?: boolean;
7
+ /** Snippet opcional para renderizar o conteúdo de cada aba. */
8
+ children?: Snippet<[ProductDetailsTab]>;
9
+ };
10
+ declare const ProductDetailTabs: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type ProductDetailTabs = ReturnType<typeof ProductDetailTabs>;
12
+ export default ProductDetailTabs;
@@ -0,0 +1,35 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Lista de tags do produto (ex: localização, categoria).
4
+ * Quando `isLoading` é true, exibe Skeletons.
5
+ * @component
6
+ * @example
7
+ * ```svelte
8
+ * <ProductDetailTags tags={[{ text: "Luanda" }, { text: "Kilamba" }]} />
9
+ * ```
10
+ */
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import CardTags from "../../../core/card/shared/CardTags.svelte";
15
+ import Skeleton from "../../../ui/skeleton/skeleton.svelte";
16
+ import type { ProductDetailsTag } from "../types";
17
+
18
+ let {
19
+ tags,
20
+ isLoading = false,
21
+ }: {
22
+ tags?: ProductDetailsTag[];
23
+ isLoading?: boolean;
24
+ } = $props();
25
+ </script>
26
+
27
+ {#if isLoading}
28
+ <div class="flex flex-wrap gap-2">
29
+ {#each Array.from({ length: 3 }) as _, i (i)}
30
+ <Skeleton class="h-5 w-20 rounded-full" />
31
+ {/each}
32
+ </div>
33
+ {:else if tags?.length}
34
+ <CardTags tags={tags} isWrap />
35
+ {/if}
@@ -0,0 +1,8 @@
1
+ import type { ProductDetailsTag } from "../types";
2
+ type $$ComponentProps = {
3
+ tags?: ProductDetailsTag[];
4
+ isLoading?: boolean;
5
+ };
6
+ declare const ProductDetailTags: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type ProductDetailTags = ReturnType<typeof ProductDetailTags>;
8
+ export default ProductDetailTags;
@@ -0,0 +1,58 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Painel de compra do produto: avaliação, preço e ações.
4
+ * Compõe ProductDetailRating, ProductDetailPrice e ProductDetailActions.
5
+ * @component
6
+ * @example
7
+ * ```svelte
8
+ * <ProductPurchasePanel {data} isLoading={true} />
9
+ * ```
10
+ */
11
+ </script>
12
+
13
+ <script lang="ts">
14
+ import ProductDetailActions from "./ProductDetailActions.svelte";
15
+ import ProductDetailPrice from "./ProductDetailPrice.svelte";
16
+ import ProductDetailRating from "./ProductDetailRating.svelte";
17
+ import type { ProductDetailsData } from "../types";
18
+
19
+ let {
20
+ data,
21
+ isLoading = false,
22
+ onBuy,
23
+ onFavorite,
24
+ onCart,
25
+ }: {
26
+ data: ProductDetailsData;
27
+ isLoading?: boolean;
28
+ onBuy?: (id: string | number) => void;
29
+ onFavorite?: (id: string | number) => void;
30
+ onCart?: (id: string | number) => void;
31
+ } = $props();
32
+ </script>
33
+
34
+ <div class="flex flex-col gap-4">
35
+ <ProductDetailRating
36
+ rating={data.rating}
37
+ reviewsCount={data.reviewsCount}
38
+ {isLoading}
39
+ />
40
+
41
+ <ProductDetailPrice
42
+ newPrice={data.newPrice}
43
+ oldPrice={data.oldPrice}
44
+ currency={data.currency}
45
+ {isLoading}
46
+ />
47
+
48
+ <ProductDetailActions
49
+ id={data.id}
50
+ buttonBuyText={data.buttonBuyText}
51
+ isFavorite={data.isFavorite}
52
+ isCart={data.isCart}
53
+ {isLoading}
54
+ {onBuy}
55
+ {onFavorite}
56
+ {onCart}
57
+ />
58
+ </div>
@@ -0,0 +1,11 @@
1
+ import type { ProductDetailsData } from "../types";
2
+ type $$ComponentProps = {
3
+ data: ProductDetailsData;
4
+ isLoading?: boolean;
5
+ onBuy?: (id: string | number) => void;
6
+ onFavorite?: (id: string | number) => void;
7
+ onCart?: (id: string | number) => void;
8
+ };
9
+ declare const ProductPurchasePanel: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type ProductPurchasePanel = ReturnType<typeof ProductPurchasePanel>;
11
+ export default ProductPurchasePanel;
@@ -0,0 +1,170 @@
1
+ import type { IconSvgElement } from "@hugeicons/svelte";
2
+ /**
3
+ * Item individual do breadcrumb de navegação.
4
+ * Representa um nível hierárquico na navegação da página
5
+ * (ex: Home / Turismo / Hotel Baía Azul).
6
+ */
7
+ export interface ProductDetailsBreadcrumbItem {
8
+ /** Texto visível do item no breadcrumb. */
9
+ label: string;
10
+ /** URL de destino quando o item é clicável (último item geralmente não tem href). */
11
+ href?: string;
12
+ }
13
+ /**
14
+ * Tag/badge associada ao produto.
15
+ * Usada para destacar categorias, localização ou características rápidas.
16
+ */
17
+ export interface ProductDetailsTag {
18
+ /** Ícone opcional exibido antes do texto da tag. */
19
+ icon?: IconSvgElement;
20
+ /** Texto principal da tag (ex: "Luanda", "5 Estrelas"). */
21
+ text: string;
22
+ }
23
+ /**
24
+ * Imagem da galeria do produto.
25
+ * Cada imagem pode ter um texto alternativo para acessibilidade.
26
+ */
27
+ export interface ProductDetailsImage {
28
+ /** Caminho ou URL completa da imagem (obrigatório). */
29
+ src: string;
30
+ /** Texto alternativo para acessibilidade (recomendado). */
31
+ alt?: string;
32
+ }
33
+ /**
34
+ * Informações de localização do produto.
35
+ * Usada para exibir o mapa embed e detalhes do endereço.
36
+ */
37
+ export interface ProductDetailsLocation {
38
+ /** Nome/título da localização (ex: "Morro da Luz"). */
39
+ label?: string;
40
+ /** Endereço completo ou parcial para exibição. */
41
+ address?: string;
42
+ /**
43
+ * URL do embed do mapa.
44
+ * Suporta OpenStreetMap, Google Maps e outros serviços de embed.
45
+ * Se não for fornecido, usa um mapa padrão.
46
+ */
47
+ mapUrl?: string;
48
+ }
49
+ /**
50
+ * Avaliação de um cliente sobre o produto.
51
+ * Contém autor, classificação, data e comentário.
52
+ */
53
+ export interface ProductDetailsReview {
54
+ /** Identificador único da avaliação (usado como key nos {#each}). */
55
+ id: string | number;
56
+ /** Nome do autor da avaliação. */
57
+ author: string;
58
+ /**
59
+ * Classificação numérica (ex: 4.5 de 5).
60
+ * Geralmente um valor entre 0 e 5.
61
+ */
62
+ rating: number;
63
+ /** Data da avaliação (formato string, ex: "12/08/2024"). */
64
+ date?: string;
65
+ /** Comentário/texto completo da avaliação. */
66
+ comment: string;
67
+ }
68
+ /**
69
+ * Aba de conteúdo adicional no produto.
70
+ * Usada para organizar descrição, avaliações, detalhes técnicos, etc.
71
+ */
72
+ export interface ProductDetailsTab {
73
+ /** Identificador único da aba (ex: "description", "reviews"). */
74
+ id: string;
75
+ /** Rótulo visível no botão da aba. */
76
+ label: string;
77
+ }
78
+ /**
79
+ * Dados completos apresentados na página de detalhes do produto.
80
+ * Interface principal que agrega todas as informações necessárias
81
+ * para renderizar qualquer variante do componente.
82
+ */
83
+ export interface ProductDetailsData {
84
+ /** Identificador único do produto. */
85
+ id: string | number;
86
+ /** Itens do breadcrumb de navegação. */
87
+ breadcrumb: ProductDetailsBreadcrumbItem[];
88
+ /** Título principal do produto (nome). */
89
+ title: string;
90
+ /** Subtítulo opcional (slogan ou descrição curta). */
91
+ subtitle?: string;
92
+ /** Descrição curta do produto. */
93
+ description: string;
94
+ /** Descrição longa/detalhada (opcional). */
95
+ longDescription?: string;
96
+ /** Lista de tags/categorias associadas ao produto. */
97
+ tags?: ProductDetailsTag[];
98
+ /** Galeria de imagens do produto. */
99
+ gallery: ProductDetailsImage[];
100
+ /** Informações de localização para exibir no mapa. */
101
+ location?: ProductDetailsLocation;
102
+ /**
103
+ * Classificação média do produto.
104
+ * Valor entre 0 e 5 (ex: 4.2).
105
+ */
106
+ rating?: number;
107
+ /** Número total de avaliações recebidas. */
108
+ reviewsCount?: number;
109
+ /** Lista de avaliações individuais dos clientes. */
110
+ reviews?: ProductDetailsReview[];
111
+ /** Preço atual/promocional do produto. */
112
+ newPrice?: string | number;
113
+ /** Preço original (antes do desconto). */
114
+ oldPrice?: string | number;
115
+ /** Símbolo da moeda (ex: "Kz", "€", "$"). */
116
+ currency?: string;
117
+ /** Quantidade disponível em stock. */
118
+ quantityAvailable?: number;
119
+ /** Lista de características/features do produto (strings). */
120
+ features?: string[];
121
+ /**
122
+ * Abas de conteúdo personalizadas.
123
+ * Se não for fornecido, usa padrão: Descrição + Avaliações.
124
+ */
125
+ tabs?: ProductDetailsTab[];
126
+ /** Texto customizado para o botão de compra (padrão: "Comprar"). */
127
+ buttonBuyText?: string;
128
+ /** Estado inicial do produto no carrinho. */
129
+ isCart?: boolean;
130
+ /** Estado inicial do produto nos favoritos. */
131
+ isFavorite?: boolean;
132
+ }
133
+ /**
134
+ * Propriedades do componente principal ProductDetails.
135
+ * Suporta quatro variantes de layout e callbacks de interação.
136
+ */
137
+ export interface ProductDetailsProps {
138
+ /**
139
+ * Variante do layout da página de detalhes.
140
+ * - 1: Duas colunas (galeria à esquerda, info à direita)
141
+ * - 2: Galeria em destaque com conteúdo centralizado
142
+ * - 3: Painel de compra sticky no lado direito
143
+ * - 4: Abas de conteúdo (descrição e avaliações) abaixo da info
144
+ * @default 1
145
+ */
146
+ varient?: 1 | 2 | 3 | 4;
147
+ /** Dados completos do produto a serem exibidos. */
148
+ data: ProductDetailsData;
149
+ /**
150
+ * Estado de carregamento.
151
+ * Quando true, todos os blocos exibem Skeletons no lugar do conteúdo.
152
+ * @default false
153
+ */
154
+ isLoading?: boolean;
155
+ /**
156
+ * Callback executado quando o utilizador clica no botão de compra.
157
+ * Recebe o id do produto como argumento.
158
+ */
159
+ onBuy?: (id: string | number) => void;
160
+ /**
161
+ * Callback executado quando o utilizador clica no botão de favorito.
162
+ * Recebe o id do produto como argumento.
163
+ */
164
+ onFavorite?: (id: string | number) => void;
165
+ /**
166
+ * Callback executado quando o utilizador clica no botão de carrinho.
167
+ * Recebe o id do produto como argumento.
168
+ */
169
+ onCart?: (id: string | number) => void;
170
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,113 @@
1
+ <script lang="ts" module>
2
+ /**
3
+ * Variante 01 da página de perfil. Reutiliza headers partilhados,
4
+ * tabs, moradas, segurança e definições.
5
+ * @component
6
+ */
7
+ </script>
8
+
9
+ <script lang="ts">
10
+ import { t } from "../../../../i18n";
11
+ import type { ProfileUserData } from "../types";
12
+ import ProfileUserHeader from "../shared/ProfileUserHeader.svelte";
13
+ import ProfileUserTabs from "../shared/ProfileUserTabs.svelte";
14
+ import ProfileUserAddress from "../shared/ProfileUserAddress.svelte";
15
+ import ProfileUserResetPassword from "../shared/ProfileUserResetPassword.svelte";
16
+ import ProfileUserSetting from "../shared/ProfileUserSetting.svelte";
17
+
18
+ let {
19
+ data,
20
+ activeTab = $bindable(),
21
+ isLoading = false,
22
+ onLoginAsUser,
23
+ onAddAddress,
24
+ onChangePassword,
25
+ tabs: customTabs,
26
+ // Header V1
27
+ statusDefault = $t("profile-user.variant1.statusActive"),
28
+ roleDefault = $t("profile-user.variant1.roleAdministrator"),
29
+ loginAsUserLabel = $t("profile-user.variant1.loginAsUser"),
30
+ // Address
31
+ addressesTitle = $t("profile-user.variant1.addresses.title"),
32
+ addressesAddLabel = $t("profile-user.variant1.addresses.add"),
33
+ addressesDefaultBadge = $t("profile-user.variant1.addresses.defaultBadge"),
34
+ addressesEmptyText = $t("profile-user.variant1.addresses.empty"),
35
+ }: {
36
+ data: ProfileUserData;
37
+ activeTab?: string;
38
+ isLoading?: boolean;
39
+ onLoginAsUser?: (id: string | number) => void;
40
+ onAddAddress?: () => void;
41
+ onChangePassword?: (payload: {
42
+ currentPassword: string;
43
+ newPassword: string;
44
+ confirmPassword: string;
45
+ }) => void;
46
+ tabs?: { id: string; label: string }[];
47
+ // Header V1
48
+ statusDefault?: string;
49
+ roleDefault?: string;
50
+ loginAsUserLabel?: string;
51
+ // Address
52
+ addressesTitle?: string;
53
+ addressesAddLabel?: string;
54
+ addressesDefaultBadge?: string;
55
+ addressesEmptyText?: string;
56
+ } = $props();
57
+
58
+ const defaultTabs = $derived(
59
+ customTabs?.length
60
+ ? customTabs
61
+ : [
62
+ { id: "addresses", label: $t("profile-user.variant1.tabs.addresses") || "Moradas" },
63
+ { id: "security", label: $t("profile-user.variant2.tabs.security") || "Segurança" },
64
+ { id: "settings", label: $t("profile-user.variant1.tabs.settings") || "Definições" },
65
+ ]
66
+ );
67
+
68
+ const currentTab = $derived(activeTab ?? defaultTabs[0]?.id ?? "addresses");
69
+ const fullName = $derived(data.fullName ?? `${data.firstName} ${data.lastName}`);
70
+ </script>
71
+
72
+ <div class="bg-background">
73
+ <ProfileUserHeader
74
+ {data}
75
+ {isLoading}
76
+ {fullName}
77
+ {onLoginAsUser}
78
+ {statusDefault}
79
+ {roleDefault}
80
+ {loginAsUserLabel}
81
+ />
82
+
83
+ <div class="mx-auto max-w-7xl px-6 py-8">
84
+ <ProfileUserTabs
85
+ tabs={defaultTabs}
86
+ bind:activeTab
87
+ {isLoading}
88
+ />
89
+
90
+ <div class="flex flex-col gap-8">
91
+ {#if currentTab === "addresses"}
92
+ <ProfileUserAddress
93
+ addresses={data.addresses}
94
+ {isLoading}
95
+ {onAddAddress}
96
+ title={addressesTitle}
97
+ addLabel={addressesAddLabel}
98
+ defaultBadge={addressesDefaultBadge}
99
+ emptyText={addressesEmptyText}
100
+ />
101
+ {:else if currentTab === "security"}
102
+ <ProfileUserResetPassword {isLoading} {onChangePassword} />
103
+ {:else if currentTab === "settings"}
104
+ <ProfileUserSetting
105
+ notificationPref={data.notificationPref}
106
+ enableNotification={data.enableNotification}
107
+ marketingPrefs={data.marketingPrefs}
108
+ {isLoading}
109
+ />
110
+ {/if}
111
+ </div>
112
+ </div>
113
+ </div>
@@ -0,0 +1,27 @@
1
+ import type { ProfileUserData } from "../types";
2
+ type $$ComponentProps = {
3
+ data: ProfileUserData;
4
+ activeTab?: string;
5
+ isLoading?: boolean;
6
+ onLoginAsUser?: (id: string | number) => void;
7
+ onAddAddress?: () => void;
8
+ onChangePassword?: (payload: {
9
+ currentPassword: string;
10
+ newPassword: string;
11
+ confirmPassword: string;
12
+ }) => void;
13
+ tabs?: {
14
+ id: string;
15
+ label: string;
16
+ }[];
17
+ statusDefault?: string;
18
+ roleDefault?: string;
19
+ loginAsUserLabel?: string;
20
+ addressesTitle?: string;
21
+ addressesAddLabel?: string;
22
+ addressesDefaultBadge?: string;
23
+ addressesEmptyText?: string;
24
+ };
25
+ declare const ProfileUser01: import("svelte").Component<$$ComponentProps, {}, "activeTab">;
26
+ type ProfileUser01 = ReturnType<typeof ProfileUser01>;
27
+ export default ProfileUser01;