negodesign 0.0.35 → 0.0.37

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 (95) hide show
  1. package/dist/components/core/card/product/01/CardProduct01.svelte +130 -0
  2. package/dist/components/core/card/product/01/CardProduct01.svelte.d.ts +4 -0
  3. package/dist/components/core/card/product/02/CardProduct02.svelte +118 -0
  4. package/dist/components/core/card/product/02/CardProduct02.svelte.d.ts +4 -0
  5. package/dist/components/core/card/product/CardProduct.svelte +22 -0
  6. package/dist/components/core/card/product/CardProduct.svelte.d.ts +11 -0
  7. package/dist/components/core/card/product/data.d.ts +3 -0
  8. package/dist/components/core/card/product/data.js +89 -0
  9. package/dist/components/core/card/profile/01/CardProfile01.svelte +0 -1
  10. package/dist/components/core/card/promotion/CardPromotion.svelte +1 -1
  11. package/dist/components/core/card/promotion/CardPromotionPrice.svelte.d.ts +8 -0
  12. package/dist/components/core/card/shared/CardPhoneOrWhatsapp.svelte +12 -5
  13. package/dist/components/core/card/shared/CardTags.svelte +49 -24
  14. package/dist/components/core/card/shared/CardTitlePrice.svelte +24 -0
  15. package/dist/components/core/card/shared/CardTitlePrice.svelte.d.ts +15 -0
  16. package/dist/components/core/card/shared/TruncatableText.svelte +1 -1
  17. package/dist/components/core/card/types.d.ts +48 -0
  18. package/dist/components/core/carousel/grid/media/ui/CarouselGridMedia.svelte +38 -28
  19. package/dist/components/core/carousel/grid/product/types.d.ts +22 -0
  20. package/dist/components/core/carousel/grid/product/types.js +1 -0
  21. package/dist/components/core/carousel/grid/product/ui/CarouselGridProduct.svelte +71 -0
  22. package/dist/components/core/carousel/grid/product/ui/CarouselGridProduct.svelte.d.ts +4 -0
  23. package/dist/components/core/carousel/grid/profile/ui/CarouselGridProfile.svelte +56 -46
  24. package/dist/components/core/carousel/grid/promotion/types.d.ts +23 -0
  25. package/dist/components/core/carousel/grid/promotion/types.js +1 -0
  26. package/dist/components/core/carousel/grid/promotion/ui/CarouselGridPromotion.svelte +68 -0
  27. package/dist/components/core/carousel/grid/promotion/ui/CarouselGridPromotion.svelte.d.ts +4 -0
  28. package/dist/components/core/carousel/highlights/ui/CarouselHighlights.svelte +57 -47
  29. package/dist/components/core/carousel/product/types.d.ts +21 -0
  30. package/dist/components/core/carousel/product/types.js +1 -0
  31. package/dist/components/core/carousel/product/ui/CarouselProduct.svelte +57 -0
  32. package/dist/components/core/carousel/product/ui/CarouselProduct.svelte.d.ts +4 -0
  33. package/dist/components/core/carousel/promotion/ui/CarouselPromotion.svelte +57 -47
  34. package/dist/components/core/panel/NotFoundEmpty.svelte +76 -0
  35. package/dist/components/core/panel/NotFoundEmpty.svelte.d.ts +16 -0
  36. package/dist/components/pages/company-profile/CompanyProfile.svelte +144 -0
  37. package/dist/components/pages/company-profile/CompanyProfile.svelte.d.ts +4 -0
  38. package/dist/components/pages/company-profile/data.d.ts +2 -0
  39. package/dist/components/pages/company-profile/data.js +115 -0
  40. package/dist/components/pages/company-profile/shared/ProfileAboutCompany.svelte +79 -0
  41. package/dist/components/pages/company-profile/shared/ProfileAboutCompany.svelte.d.ts +9 -0
  42. package/dist/components/pages/company-profile/shared/ProfileBanner.svelte +270 -0
  43. package/dist/components/pages/company-profile/shared/ProfileBanner.svelte.d.ts +14 -0
  44. package/dist/components/pages/company-profile/shared/ProfileGallery.svelte +198 -0
  45. package/dist/components/pages/company-profile/shared/ProfileGallery.svelte.d.ts +9 -0
  46. package/dist/components/pages/company-profile/shared/ProfileMenuBadge.svelte +56 -0
  47. package/dist/components/pages/company-profile/shared/ProfileMenuBadge.svelte.d.ts +11 -0
  48. package/dist/components/pages/company-profile/shared/ProfileProducts.svelte +62 -0
  49. package/dist/components/pages/company-profile/shared/ProfileProducts.svelte.d.ts +9 -0
  50. package/dist/components/pages/company-profile/shared/ProfileSidebar.svelte +57 -0
  51. package/dist/components/pages/company-profile/shared/ProfileSidebar.svelte.d.ts +11 -0
  52. package/dist/components/pages/company-profile/types.d.ts +137 -0
  53. package/dist/components/pages/company-profile/types.js +1 -0
  54. package/dist/components/pages/privacy-policy-or-terms-of-use/data.d.ts +3 -0
  55. package/dist/components/pages/privacy-policy-or-terms-of-use/data.js +179 -0
  56. package/dist/components/pages/privacy-policy-or-terms-of-use/shared/DocSectionView.svelte +2 -1
  57. package/dist/components/pages/privacy-policy-or-terms-of-use/shared/DocSectionView.svelte.d.ts +1 -0
  58. package/dist/components/pages/product-details/01/ProductDetails01.svelte +19 -7
  59. package/dist/components/pages/product-details/02/ProductDetails02.svelte +42 -37
  60. package/dist/components/pages/product-details/03/ProductDetails03.svelte +33 -29
  61. package/dist/components/pages/product-details/ProductDetails.svelte +11 -7
  62. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte +11 -5
  63. package/dist/components/pages/product-details/shared/ProductDetailFeatures.svelte.d.ts +2 -1
  64. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte +52 -20
  65. package/dist/components/pages/product-details/shared/ProductDetailFilesVertical.svelte.d.ts +1 -1
  66. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte +51 -20
  67. package/dist/components/pages/product-details/shared/ProductDetailImage.svelte.d.ts +1 -1
  68. package/dist/components/pages/product-details/shared/ProductDetailPrice.svelte +1 -1
  69. package/dist/components/pages/product-details/types.d.ts +9 -2
  70. package/dist/components/pages/profile-user/types.d.ts +7 -0
  71. package/dist/components/ui/empty/empty-content.svelte +23 -0
  72. package/dist/components/ui/empty/empty-content.svelte.d.ts +5 -0
  73. package/dist/components/ui/empty/empty-description.svelte +23 -0
  74. package/dist/components/ui/empty/empty-description.svelte.d.ts +5 -0
  75. package/dist/components/ui/empty/empty-header.svelte +20 -0
  76. package/dist/components/ui/empty/empty-header.svelte.d.ts +5 -0
  77. package/dist/components/ui/empty/empty-media.svelte +41 -0
  78. package/dist/components/ui/empty/empty-media.svelte.d.ts +26 -0
  79. package/dist/components/ui/empty/empty-title.svelte +20 -0
  80. package/dist/components/ui/empty/empty-title.svelte.d.ts +5 -0
  81. package/dist/components/ui/empty/empty.svelte +23 -0
  82. package/dist/components/ui/empty/empty.svelte.d.ts +5 -0
  83. package/dist/components/ui/empty/index.d.ts +7 -0
  84. package/dist/components/ui/empty/index.js +9 -0
  85. package/dist/globals.css +301 -5
  86. package/dist/i18n/langs/en.d.ts +25 -0
  87. package/dist/i18n/langs/en.js +25 -0
  88. package/dist/i18n/langs/pt.d.ts +25 -0
  89. package/dist/i18n/langs/pt.js +25 -0
  90. package/dist/i18n/translations.d.ts +50 -0
  91. package/package.json +1 -1
  92. package/dist/components/core/card/shared/CardStartPrice.svelte.d.ts +0 -8
  93. package/dist/components/pages/product-details/04/ProductDetails04.svelte +0 -65
  94. package/dist/components/pages/product-details/04/ProductDetails04.svelte.d.ts +0 -11
  95. /package/dist/components/core/card/{shared/CardStartPrice.svelte → promotion/CardPromotionPrice.svelte} +0 -0
@@ -0,0 +1,11 @@
1
+ import type { ProfileNavItem } from "../types";
2
+ type $$ComponentProps = {
3
+ items: ProfileNavItem[];
4
+ active?: string;
5
+ isLoading?: boolean;
6
+ onSelect?: (id: string) => void;
7
+ className?: string;
8
+ };
9
+ declare const ProfileSidebar: import("svelte").Component<$$ComponentProps, {}, "">;
10
+ type ProfileSidebar = ReturnType<typeof ProfileSidebar>;
11
+ export default ProfileSidebar;
@@ -0,0 +1,137 @@
1
+ import type { IconSvgElement } from "@hugeicons/svelte";
2
+ import type { CardProductProps } from "../../core/card/types";
3
+ /**
4
+ * Item de estatística exibido na faixa de dados do banner
5
+ * (ex: "Setor de atuação", "Anos de Atuação", "Localização").
6
+ */
7
+ export interface ProfileStatItem {
8
+ /** Rótulo da estatística (ex: "Setor de atuação"). */
9
+ label: string;
10
+ /** Valor exibido (ex: "Papel", "8", "Benguela"). */
11
+ value: string;
12
+ }
13
+ /**
14
+ * Imagem usada na galeria ou na capa do perfil.
15
+ */
16
+ export interface ProfileImage {
17
+ /** Caminho ou URL completa da imagem (obrigatório). */
18
+ src: string;
19
+ /** Texto alternativo para acessibilidade. */
20
+ alt?: string;
21
+ type?: "image" | "video";
22
+ }
23
+ /**
24
+ * Item de navegação usado pela Sidebar e pelo menu de badges (mobile).
25
+ * O `id` deve corresponder ao `id` da secção (elemento âncora) na página.
26
+ */
27
+ export interface ProfileNavItem {
28
+ /** Identificador único da secção (usado como âncora de scroll). */
29
+ id: string;
30
+ /** Rótulo visível do item de navegação. */
31
+ label: string;
32
+ }
33
+ /**
34
+ * Dados do banner/apresentação da empresa: capa, logótipo, nome,
35
+ * país, botão de contacto e faixa de estatísticas.
36
+ */
37
+ export interface ProfileBannerData {
38
+ address?: string;
39
+ category?: string;
40
+ website?: string;
41
+ yearFounded?: string;
42
+ /** Imagem(ns) de capa exibidas no topo do banner. */
43
+ coverImages: ProfileImage[];
44
+ /** URL do logótipo da empresa. */
45
+ logo?: string;
46
+ /** Nome da empresa/fábrica. */
47
+ name: string;
48
+ /** Nome do país (ex: "Angola"). */
49
+ countryLabel?: string;
50
+ /** Emoji ou URL da bandeira do país. */
51
+ countryFlag?: string;
52
+ /** Texto do botão de contacto (padrão: "Contactar fábrica"). */
53
+ contactLabel?: string;
54
+ /** Estatísticas rápidas exibidas por baixo do nome (sector, anos, etc). */
55
+ stats?: ProfileStatItem[];
56
+ }
57
+ /**
58
+ * Dados da secção "Sobre a Empresa".
59
+ */
60
+ export interface ProfileAboutData {
61
+ /** Imagem ilustrativa da empresa (ex: fachada, fábrica). */
62
+ image?: string;
63
+ /** Texto alternativo da imagem. */
64
+ imageAlt?: string;
65
+ /** Título da secção (padrão: "Sobre a Empresa"). */
66
+ title?: string;
67
+ /** Parágrafos de texto descritivo da empresa. */
68
+ paragraphs: string[];
69
+ }
70
+ /**
71
+ * Dados da secção "Galeria".
72
+ */
73
+ export interface ProfileGalleryData {
74
+ /** Título da secção (padrão: "Galeria da {name}"). */
75
+ title?: string;
76
+ /** Lista de imagens/vídeos da galeria. */
77
+ images: ProfileImage[];
78
+ }
79
+ /**
80
+ * Dados da secção "Produtos".
81
+ */
82
+ export interface ProfileProductsData {
83
+ /** Título da secção (padrão: "Produtos"). */
84
+ title?: string;
85
+ /** Descrição da secção de produtos. */
86
+ description?: string;
87
+ /** Lista de produtos/serviços da empresa. */
88
+ items: CardProductProps[];
89
+ /**
90
+ * Estado de carregamento.
91
+ * Quando true, todos os blocos exibem Skeletons no lugar do conteúdo.
92
+ * @default false
93
+ */
94
+ isLoading?: boolean;
95
+ /** Variante do CardProduct (1 ou 2). */
96
+ variant?: 1 | 2;
97
+ varient?: 1 | 2;
98
+ }
99
+ /**
100
+ * Dados completos apresentados na página de perfil da empresa/fábrica.
101
+ */
102
+ export interface CompanyProfileData {
103
+ /** Identificador único do perfil. */
104
+ id: string | number;
105
+ /** Dados do banner (capa, nome, estatísticas). */
106
+ banner: ProfileBannerData;
107
+ /** Dados da secção "Sobre a Empresa". */
108
+ about: ProfileAboutData;
109
+ /** Dados da secção "Galeria". */
110
+ gallery: ProfileGalleryData;
111
+ /** Dados da secção "Produtos". */
112
+ products?: ProfileProductsData;
113
+ }
114
+ /**
115
+ * Propriedades do componente principal CompanyProfile.
116
+ */
117
+ export interface CompanyProfileProps {
118
+ /** Dados completos do perfil a serem exibidos. */
119
+ data?: CompanyProfileData;
120
+ /**
121
+ * Estado de carregamento.
122
+ * Quando true, todos os blocos exibem Skeletons no lugar do conteúdo.
123
+ * @default false
124
+ */
125
+ isLoading?: boolean;
126
+ onWhatsapp?: (id: string | number) => void;
127
+ onEmail?: (id: string | number) => void;
128
+ /** Callback executado quando o utilizador clica em copiar link. */
129
+ onCopyLink?: (id: string | number) => void;
130
+ /** Callback executado quando o utilizador clica em partilhar. */
131
+ onShare?: (id: string | number) => void;
132
+ }
133
+ export interface ProfileBannerTag {
134
+ label: string;
135
+ value: string;
136
+ icon?: string | IconSvgElement;
137
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { DocPageData } from "./types";
2
+ export declare const privacyPolicyData: DocPageData;
3
+ export default privacyPolicyData;
@@ -0,0 +1,179 @@
1
+ export const privacyPolicyData = {
2
+ title: "Política de Privacidade e Termos de Uso",
3
+ lastUpdated: "15 de Agosto de 2026",
4
+ breadcrumb: [
5
+ { label: "Início", href: "/" },
6
+ { label: "Institucional", href: "#" },
7
+ { label: "Termos & Privacidade" },
8
+ ],
9
+ sections: [
10
+ {
11
+ id: "introducao",
12
+ number: "1",
13
+ title: "Introdução",
14
+ level: 1,
15
+ blocks: [
16
+ {
17
+ type: "paragraph",
18
+ text: "Bem-vindo à plataforma Negoturismo. A sua privacidade e a transparência nas relações com os nossos utilizadores, parceiros e clientes são valores fundamentais para a nossa organização.",
19
+ },
20
+ {
21
+ type: "paragraph",
22
+ text: "Este documento estabelece as diretrizes de utilização da plataforma digital Negoturismo, bem como as políticas de recolha, tratamento e proteção dos dados pessoais fornecidos durante o acesso aos nossos serviços turísticos, reservas de hotelaria, voos e contratação de guias e tradutores.",
23
+ },
24
+ ],
25
+ subsections: [
26
+ {
27
+ id: "aceitacao-termos",
28
+ number: "1.1",
29
+ title: "Aceitação dos Termos",
30
+ level: 2,
31
+ blocks: [
32
+ {
33
+ type: "paragraph",
34
+ text: "Ao aceder, navegar ou utilizar qualquer funcionalidade do portal Negoturismo, o utilizador declara expressamente que leu, compreendeu e concorda com todas as disposições aqui estipuladas.",
35
+ },
36
+ {
37
+ type: "list",
38
+ items: [
39
+ "Utilizadores registados têm acesso a funcionalidades exclusivas de gestão de reservas.",
40
+ "Parceiros comerciais e fornecedores de serviços sujeitam-se ainda aos contratos específicos de afiliação.",
41
+ "O uso continuado da plataforma após eventuais alterações implica aceitação tácita das novas condições.",
42
+ ],
43
+ },
44
+ ],
45
+ },
46
+ ],
47
+ },
48
+ {
49
+ id: "recolha-dados",
50
+ number: "2",
51
+ title: "Recolha e Tratamento de Dados Pessoais",
52
+ level: 1,
53
+ blocks: [
54
+ {
55
+ type: "paragraph",
56
+ text: "Para garantir a prestação eficaz dos nossos serviços e proporcionar uma experiência personalizada, recolhemos dados necessários para processamento de pagamentos, validação de identidade e emissão de vouchers de viagem.",
57
+ },
58
+ ],
59
+ subsections: [
60
+ {
61
+ id: "dados-fornecidos",
62
+ number: "2.1",
63
+ title: "Dados Fornecidos Diretamente",
64
+ level: 2,
65
+ blocks: [
66
+ {
67
+ type: "list",
68
+ items: [
69
+ "Nome completo, número de identificação civil ou passaporte.",
70
+ "Endereço de correio eletrónico, número de telefone e morada de faturação.",
71
+ "Informações de pagamento (processadas de forma encriptada através de gateways certificados).",
72
+ "Preferências de viagem, itinerários e histórico de reservas efetuadas.",
73
+ ],
74
+ },
75
+ ],
76
+ },
77
+ {
78
+ id: "finalidade-tratamento",
79
+ number: "2.2",
80
+ title: "Finalidades do Tratamento",
81
+ level: 2,
82
+ blocks: [
83
+ {
84
+ type: "paragraph",
85
+ text: "Os dados recolhidos destinam-se exclusivamente às seguintes finalidades legítimas:",
86
+ },
87
+ {
88
+ type: "list",
89
+ items: [
90
+ "Processamento e confirmação de reservas junto a companhias aéreas, hotéis e parceiros locais.",
91
+ "Comunicação com o cliente sobre alterações de voos, confirmações e avisos de suporte ao cliente.",
92
+ "Prevenção contra fraudes e garantia da segurança operacional da plataforma.",
93
+ "Envio de ofertas e recomendações personalizadas quando previamente autorizado pelo utilizador.",
94
+ ],
95
+ },
96
+ ],
97
+ },
98
+ ],
99
+ },
100
+ {
101
+ id: "reservas-pagamentos",
102
+ number: "3",
103
+ title: "Reservas, Preços e Pagamentos",
104
+ level: 1,
105
+ blocks: [
106
+ {
107
+ type: "paragraph",
108
+ text: "Todas as transações financeiras efetuadas na plataforma são expressas na moeda indicada no momento da compra (Kwanza - AOA ou moeda estrangeira correspondente) e incluem todas as taxas aplicáveis por lei.",
109
+ },
110
+ {
111
+ type: "list",
112
+ items: [
113
+ "A confirmação da reserva está sujeita à validação do pagamento pela instituição financeira emissora.",
114
+ "Preços e disponibilidades de quartos e voos são dinâmicos e sujeitos a alteração até à finalização da compra.",
115
+ "O cliente é responsável pela verificação da exatidão dos dados antes da confirmação do pagamento.",
116
+ ],
117
+ },
118
+ ],
119
+ },
120
+ {
121
+ id: "cancelamentos-reembolsos",
122
+ number: "4",
123
+ title: "Cancelamentos e Reembolsos",
124
+ level: 1,
125
+ blocks: [
126
+ {
127
+ type: "paragraph",
128
+ text: "As condições de cancelamento, alteração de datas e direito a reembolso variam de acordo com as políticas individuais de cada fornecedor de serviços (hotel, companhia aérea, operadora de turismo).",
129
+ },
130
+ {
131
+ type: "list",
132
+ items: [
133
+ "Tarifas não-reembolsáveis não conferem direito à devolução dos valores pagos em caso de desistência.",
134
+ "Pedidos de cancelamento dentro do prazo legal são processados num prazo estimado de 5 a 15 dias úteis.",
135
+ "Eventuais taxas administrativas de intermediação poderão ser deduzidas do montante a reembolsar conforme o regulamento da tarifa.",
136
+ ],
137
+ },
138
+ ],
139
+ },
140
+ {
141
+ id: "seguranca-cookies",
142
+ number: "5",
143
+ title: "Segurança da Informação e Cookies",
144
+ level: 1,
145
+ blocks: [
146
+ {
147
+ type: "paragraph",
148
+ text: "Implementamos protocolos de segurança modernos, incluindo encriptação TLS/SSL e padrões de controlo de acesso rigorosos para proteger os seus dados contra acessos não autorizados, perda ou destruição.",
149
+ },
150
+ {
151
+ type: "paragraph",
152
+ text: "Utilizamos cookies de sessão e analíticos para melhorar a navegação, memorizar preferências e compreender como os nossos serviços são utilizados pelos visitantes.",
153
+ },
154
+ ],
155
+ },
156
+ {
157
+ id: "contacto-suporte",
158
+ number: "6",
159
+ title: "Contactos e Apoio ao Cliente",
160
+ level: 1,
161
+ blocks: [
162
+ {
163
+ type: "paragraph",
164
+ text: "Caso tenha qualquer dúvida sobre esta Política de Privacidade ou pretenda exercer os seus direitos de acesso, retificação ou eliminação de dados, contacte a nossa equipa de encarregados de proteção de dados:",
165
+ },
166
+ {
167
+ type: "list",
168
+ items: [
169
+ "E-mail: privacidade@negoturismo.ao",
170
+ "Suporte Geral: suporte@negoturismo.ao",
171
+ "Telefone: +244 923 000 000 / +244 945 000 000",
172
+ "Endereço: Luanda, Angola",
173
+ ],
174
+ },
175
+ ],
176
+ },
177
+ ],
178
+ };
179
+ export default privacyPolicyData;
@@ -12,6 +12,7 @@
12
12
  <script lang="ts">
13
13
  import type { DocSection } from "../types";
14
14
  import ContentBlockView from "./ContentBlockView.svelte";
15
+ import DocSectionView from "./DocSectionView.svelte";
15
16
 
16
17
  let { section }: { section: DocSection } = $props();
17
18
 
@@ -37,7 +38,7 @@
37
38
  {#if section.subsections}
38
39
  <div class="flex flex-col gap-6 mt-2">
39
40
  {#each section.subsections as subsection (subsection.id)}
40
- <svelte:self section={subsection} />
41
+ <DocSectionView section={subsection} />
41
42
  {/each}
42
43
  </div>
43
44
  {/if}
@@ -1,4 +1,5 @@
1
1
  import type { DocSection } from "../types";
2
+ import DocSectionView from "./DocSectionView.svelte";
2
3
  type $$ComponentProps = {
3
4
  section: DocSection;
4
5
  };
@@ -17,6 +17,7 @@
17
17
  import ProductDetailHeader from "../shared/ProductDetailHeader.svelte";
18
18
  import ProductDetailImage from "../shared/ProductDetailImage.svelte";
19
19
  import ProductDetailMap from "../shared/ProductDetailMap.svelte";
20
+ import ProductDetailReviews from "../shared/ProductDetailReviews.svelte";
20
21
  import ProductDetailTags from "../shared/ProductDetailTags.svelte";
21
22
  import ProductPurchasePanel from "../shared/ProductPurchasePanel.svelte";
22
23
  import type { ProductDetailsData } from "../types";
@@ -42,10 +43,14 @@
42
43
  </div>
43
44
 
44
45
  <div class="grid gap-10 lg:grid-cols-2">
45
- <ProductDetailImage images={data.gallery} {isLoading} />
46
+ <ProductDetailImage files={data.gallery} {isLoading} />
46
47
 
47
48
  <div class="flex flex-col gap-5">
48
- <ProductDetailHeader title={data.title} subtitle={data.subtitle} {isLoading} />
49
+ <ProductDetailHeader
50
+ title={data.title}
51
+ subtitle={data.subtitle}
52
+ {isLoading}
53
+ />
49
54
 
50
55
  <ProductDetailTags tags={data.tags} {isLoading} />
51
56
 
@@ -63,9 +68,16 @@
63
68
  </div>
64
69
  </div>
65
70
 
66
- {#if data.location}
67
- <div class="mt-12">
68
- <ProductDetailMap location={data.location} {isLoading} />
69
- </div>
70
- {/if}
71
+ <div class="grid gap-10 grid-cols-1 md:grid-cols-2 mt-5 md:mt-10">
72
+ {#if data.reviews}
73
+ <div>
74
+ <ProductDetailReviews reviews={data.reviews} {isLoading} />
75
+ </div>
76
+ {/if}
77
+ {#if data.location}
78
+ <div>
79
+ <ProductDetailMap location={data.location} {isLoading} />
80
+ </div>
81
+ {/if}
82
+ </div>
71
83
  </div>
@@ -1,11 +1,11 @@
1
1
  <script lang="ts" module>
2
2
  /**
3
- * Variante 02 da página de detalhes do produto.
4
- * Layout com galeria em destaque e conteúdo em coluna centralizada.
3
+ * Variante 03 da página de detalhes do produto.
4
+ * Layout com galeria, conteúdo expandido e painel de compra sticky.
5
5
  * @component
6
6
  * @example
7
7
  * ```svelte
8
- * <ProductDetails02 {data} {onBuy} {onFavorite} {onCart} />
8
+ * <ProductDetails03 {data} {onBuy} {onFavorite} {onCart} />
9
9
  * ```
10
10
  */
11
11
  </script>
@@ -17,6 +17,7 @@
17
17
  import ProductDetailFilesVertical from "../shared/ProductDetailFilesVertical.svelte";
18
18
  import ProductDetailHeader from "../shared/ProductDetailHeader.svelte";
19
19
  import ProductDetailMap from "../shared/ProductDetailMap.svelte";
20
+ import ProductDetailReviews from "../shared/ProductDetailReviews.svelte";
20
21
  import ProductDetailTags from "../shared/ProductDetailTags.svelte";
21
22
  import ProductPurchasePanel from "../shared/ProductPurchasePanel.svelte";
22
23
  import type { ProductDetailsData } from "../types";
@@ -36,52 +37,56 @@
36
37
  } = $props();
37
38
  </script>
38
39
 
39
- <div class="bg-background">
40
- <ProductDetailFilesVertical
41
- images={data.gallery}
42
- className="mx-auto max-w-5xl px-6 pt-10"
43
- {isLoading}
44
- />
40
+ <div class="mx-auto max-w-7xl px-6 py-10">
41
+ <div class="mb-8">
42
+ <ProductDetailBreadcrumb items={data.breadcrumb} {isLoading} />
43
+ </div>
45
44
 
46
- <div class="mx-auto max-w-3xl px-6 py-12">
47
- <div class="flex flex-col gap-6">
48
- <div class="flex justify-center">
49
- <ProductDetailBreadcrumb items={data.breadcrumb} {isLoading} />
50
- </div>
45
+ <div class="grid gap-10 lg:grid-cols-[1fr_26rem]">
46
+ <div class="flex flex-col gap-8">
47
+ <ProductDetailFilesVertical files={data.gallery} {isLoading} />
51
48
 
52
- <ProductDetailHeader
53
- title={data.title}
54
- subtitle={data.subtitle}
55
- className="items-center text-center"
56
- {isLoading}
57
- />
49
+ <div class="flex flex-col gap-5">
50
+ <ProductDetailHeader
51
+ title={data.title}
52
+ subtitle={data.subtitle}
53
+ {isLoading}
54
+ />
58
55
 
59
- <div class="flex justify-center">
60
56
  <ProductDetailTags tags={data.tags} {isLoading} />
61
- </div>
62
57
 
63
- <ProductDetailDescription
64
- description={data.description}
65
- longDescription={data.longDescription}
66
- {isLoading}
67
- />
58
+ <ProductDetailDescription
59
+ description={data.description}
60
+ longDescription={data.longDescription}
61
+ {isLoading}
62
+ />
68
63
 
69
- {#if data.features}
70
- <div class="mx-auto w-full max-w-md">
64
+ {#if data.features}
71
65
  <ProductDetailFeatures features={data.features} {isLoading} />
72
- </div>
73
- {/if}
74
-
75
- <div class="flex justify-center">
76
- <div class="w-full max-w-sm">
77
- <ProductPurchasePanel {data} {isLoading} {onBuy} {onFavorite} {onCart} />
78
- </div>
66
+ {/if}
79
67
  </div>
80
68
  </div>
69
+
70
+ <aside class="lg:sticky lg:top-8 lg:self-start">
71
+ <div class="rounded-lg border p-6">
72
+ <ProductPurchasePanel
73
+ {data}
74
+ {isLoading}
75
+ {onBuy}
76
+ {onFavorite}
77
+ {onCart}
78
+ />
79
+ </div>
80
+ {#if data.reviews}
81
+ <div class="mt-5 md:mt-10">
82
+ <ProductDetailReviews reviews={data.reviews} {isLoading} />
83
+ </div>
84
+ {/if}
85
+ </aside>
81
86
  </div>
82
87
 
83
88
  {#if data.location}
84
- <div class="mx-auto max-w-3xl px-6 pb-12">
89
+ <div class="mt-12">
85
90
  <ProductDetailMap location={data.location} {isLoading} />
86
91
  </div>
87
92
  {/if}
@@ -1,25 +1,26 @@
1
1
  <script lang="ts" module>
2
2
  /**
3
- * Variante 03 da página de detalhes do produto.
4
- * Layout com galeria, conteúdo expandido e painel de compra sticky.
3
+ * Variante 04 da página de detalhes do produto.
4
+ * Layout com galeria, informações e abas de conteúdo (descrição,
5
+ * avaliações) abaixo dos dados principais.
5
6
  * @component
6
7
  * @example
7
8
  * ```svelte
8
- * <ProductDetails03 {data} {onBuy} {onFavorite} {onCart} />
9
+ * <ProductDetails04 {data} {onBuy} {onFavorite} {onCart} />
9
10
  * ```
10
11
  */
11
12
  </script>
12
13
 
13
14
  <script lang="ts">
14
15
  import ProductDetailBreadcrumb from "../shared/ProductDetailBreadcrumb.svelte";
15
- import ProductDetailDescription from "../shared/ProductDetailDescription.svelte";
16
- import ProductDetailFeatures from "../shared/ProductDetailFeatures.svelte";
17
- import ProductDetailFilesVertical from "../shared/ProductDetailFilesVertical.svelte";
18
16
  import ProductDetailHeader from "../shared/ProductDetailHeader.svelte";
17
+ import ProductDetailImage from "../shared/ProductDetailImage.svelte";
19
18
  import ProductDetailMap from "../shared/ProductDetailMap.svelte";
20
19
  import ProductDetailTags from "../shared/ProductDetailTags.svelte";
20
+ import ProductDetailTabs from "../shared/ProductDetailTabs.svelte";
21
21
  import ProductPurchasePanel from "../shared/ProductPurchasePanel.svelte";
22
22
  import type { ProductDetailsData } from "../types";
23
+ import { useDevice } from "../../../../hooks/responsive.svelte";
23
24
 
24
25
  let {
25
26
  data,
@@ -34,6 +35,7 @@
34
35
  onFavorite?: (id: string | number) => void;
35
36
  onCart?: (id: string | number) => void;
36
37
  } = $props();
38
+ const responsive = useDevice();
37
39
  </script>
38
40
 
39
41
  <div class="mx-auto max-w-7xl px-6 py-10">
@@ -41,37 +43,39 @@
41
43
  <ProductDetailBreadcrumb items={data.breadcrumb} {isLoading} />
42
44
  </div>
43
45
 
44
- <div class="grid gap-10 lg:grid-cols-[1fr_22rem]">
45
- <div class="flex flex-col gap-8">
46
- <ProductDetailFilesVertical images={data.gallery} {isLoading} />
46
+ <div class="grid gap-10 lg:grid-cols-2">
47
+ <ProductDetailImage files={data.gallery} {isLoading} />
47
48
 
48
- <div class="flex flex-col gap-5">
49
- <ProductDetailHeader title={data.title} subtitle={data.subtitle} {isLoading} />
49
+ <div class="flex flex-col gap-5">
50
+ <ProductDetailHeader
51
+ title={data.title}
52
+ subtitle={data.subtitle}
53
+ {isLoading}
54
+ />
50
55
 
51
- <ProductDetailTags tags={data.tags} {isLoading} />
56
+ <ProductDetailTags tags={data.tags} {isLoading} />
52
57
 
53
- <ProductDetailDescription
54
- description={data.description}
55
- longDescription={data.longDescription}
56
- {isLoading}
57
- />
58
+ <ProductPurchasePanel {data} {isLoading} {onBuy} {onFavorite} {onCart} />
58
59
 
59
- {#if data.features}
60
- <ProductDetailFeatures features={data.features} {isLoading} />
60
+ {#if !responsive.isMobile}
61
+ {#if data.location}
62
+ <div class="mt-5">
63
+ <ProductDetailMap location={data.location} {isLoading} />
64
+ </div>
61
65
  {/if}
62
- </div>
66
+ {/if}
63
67
  </div>
68
+ </div>
64
69
 
65
- <aside class="lg:sticky lg:top-8 lg:self-start">
66
- <div class="rounded-lg border p-6">
67
- <ProductPurchasePanel {data} {isLoading} {onBuy} {onFavorite} {onCart} />
68
- </div>
69
- </aside>
70
+ <div class="mt-12 border-t pt-8">
71
+ <ProductDetailTabs tabs={data.tabs} {data} {isLoading} />
70
72
  </div>
71
73
 
72
- {#if data.location}
73
- <div class="mt-12">
74
- <ProductDetailMap location={data.location} {isLoading} />
75
- </div>
74
+ {#if responsive.isMobile}
75
+ {#if data.location}
76
+ <div class="mt-12">
77
+ <ProductDetailMap location={data.location} {isLoading} />
78
+ </div>
79
+ {/if}
76
80
  {/if}
77
81
  </div>
@@ -20,18 +20,22 @@
20
20
  <script lang="ts">
21
21
  import type { ProductDetailsProps } from "./types";
22
22
  import ProductDetails01 from "./01/ProductDetails01.svelte";
23
- import ProductDetails02 from "./02/ProductDetails02.svelte";
24
- import ProductDetails03 from "./03/ProductDetails03.svelte";
25
- import ProductDetails04 from "./04/ProductDetails04.svelte";
23
+ import ProductDetails03 from "./02/ProductDetails02.svelte";
24
+ import ProductDetails04 from "./03/ProductDetails03.svelte";
26
25
 
27
- let { varient = 1, data, isLoading = false, onBuy, onFavorite, onCart }: ProductDetailsProps = $props();
26
+ let {
27
+ varient = 1,
28
+ data,
29
+ isLoading = false,
30
+ onBuy,
31
+ onFavorite,
32
+ onCart,
33
+ }: ProductDetailsProps = $props();
28
34
  </script>
29
35
 
30
36
  {#if varient === 2}
31
- <ProductDetails02 {data} {isLoading} {onBuy} {onFavorite} {onCart} />
32
- {:else if varient === 3}
33
37
  <ProductDetails03 {data} {isLoading} {onBuy} {onFavorite} {onCart} />
34
- {:else if varient === 4}
38
+ {:else if varient === 3}
35
39
  <ProductDetails04 {data} {isLoading} {onBuy} {onFavorite} {onCart} />
36
40
  {:else}
37
41
  <ProductDetails01 {data} {isLoading} {onBuy} {onFavorite} {onCart} />