create-snapify-app 0.0.0-stage → 1.1.0

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 (614) hide show
  1. package/LICENSE.md +24 -0
  2. package/README.md +55 -2
  3. package/bin/create-snapify-app.mjs +174 -0
  4. package/lib/args.mjs +59 -0
  5. package/lib/copy.mjs +44 -0
  6. package/lib/env.mjs +84 -0
  7. package/lib/prompt.mjs +63 -0
  8. package/package.json +33 -4
  9. package/template/.env.example +79 -0
  10. package/template/.github/workflows/ci.yml +23 -0
  11. package/template/.github/workflows/deploy.yml +26 -0
  12. package/template/.github/workflows/ping.yml +8 -0
  13. package/template/.github/workflows/release.yml +67 -0
  14. package/template/.snapify-install.json +5 -0
  15. package/template/CHANGELOG.md +28 -0
  16. package/template/LICENSE +14 -0
  17. package/template/README.md +287 -0
  18. package/template/administrator.html +23 -0
  19. package/template/apps/client/src/App.tsx +765 -0
  20. package/template/apps/client/src/auth.css +400 -0
  21. package/template/apps/client/src/lib/api.ts +22 -0
  22. package/template/apps/client/src/main.tsx +20 -0
  23. package/template/apps/community/src/App.tsx +592 -0
  24. package/template/apps/community/src/main.tsx +20 -0
  25. package/template/apps/community/src/styles.css +24 -0
  26. package/template/apps/developers/src/App.tsx +643 -0
  27. package/template/apps/developers/src/main.tsx +17 -0
  28. package/template/apps/developers/src/pages.tsx +1587 -0
  29. package/template/apps/developers/src/styles.css +98 -0
  30. package/template/apps/developers/src/ui.tsx +179 -0
  31. package/template/apps/landing/src/App.tsx +598 -0
  32. package/template/apps/landing/src/LegalPages.tsx +158 -0
  33. package/template/apps/landing/src/OrderPage.tsx +381 -0
  34. package/template/apps/landing/src/SeoPages.tsx +923 -0
  35. package/template/apps/landing/src/copy.ts +195 -0
  36. package/template/apps/landing/src/main.tsx +20 -0
  37. package/template/apps/landing/src/styles.css +803 -0
  38. package/template/apps/landing/src/types.ts +19 -0
  39. package/template/apps/marketplace/src/App.tsx +1229 -0
  40. package/template/apps/marketplace/src/developer/DeveloperApp.tsx +1266 -0
  41. package/template/apps/marketplace/src/main.tsx +20 -0
  42. package/template/apps/marketplace/src/styles.css +1001 -0
  43. package/template/apps/shared/AuthContext.tsx +65 -0
  44. package/template/apps/shared/Logo.tsx +33 -0
  45. package/template/apps/shared/api.ts +47 -0
  46. package/template/apps/shared/ui.tsx +128 -0
  47. package/template/apps/superadmin/src/App.tsx +1264 -0
  48. package/template/apps/superadmin/src/lib/api.ts +20 -0
  49. package/template/apps/superadmin/src/lib/hooks.ts +20 -0
  50. package/template/apps/superadmin/src/main.tsx +16 -0
  51. package/template/apps/superadmin/src/pages/ClientsPage.tsx +185 -0
  52. package/template/apps/superadmin/src/pages/InvoicesPage.tsx +157 -0
  53. package/template/apps/superadmin/src/pages/NewsletterPage.tsx +212 -0
  54. package/template/apps/superadmin/src/pages/OrdersPage.tsx +195 -0
  55. package/template/apps/superadmin/src/pages/PaymentsPage.tsx +150 -0
  56. package/template/apps/superadmin/src/pages/PayoutsPage.tsx +133 -0
  57. package/template/apps/superadmin/src/pages/PublishersPage.tsx +151 -0
  58. package/template/apps/superadmin/src/pages/SeoControlPage.tsx +692 -0
  59. package/template/apps/superadmin/src/pages/SettingsPage.tsx +199 -0
  60. package/template/apps/superadmin/src/pages/SystemPage.tsx +109 -0
  61. package/template/apps/superadmin/src/pages/TicketsPage.tsx +201 -0
  62. package/template/apps/superadmin/src/pages/UsersPage.tsx +167 -0
  63. package/template/client.html +21 -0
  64. package/template/community/hamdiayari/akhbar/README.md +23 -0
  65. package/template/community/hamdiayari/akhbar/screenshot.jpg +0 -0
  66. package/template/community/hamdiayari/akhbar/snapify.theme.json +76 -0
  67. package/template/community/hamdiayari/akhbar/src/Brand.tsx +26 -0
  68. package/template/community/hamdiayari/akhbar/src/Footer.tsx +182 -0
  69. package/template/community/hamdiayari/akhbar/src/Header.tsx +191 -0
  70. package/template/community/hamdiayari/akhbar/src/Layout.tsx +49 -0
  71. package/template/community/hamdiayari/akhbar/src/Social.tsx +33 -0
  72. package/template/community/hamdiayari/akhbar/src/Ticker.tsx +86 -0
  73. package/template/community/hamdiayari/akhbar/src/Views.tsx +285 -0
  74. package/template/community/hamdiayari/akhbar/src/blocks/CollectionBlocks.tsx +672 -0
  75. package/template/community/hamdiayari/akhbar/src/blocks/ContentBlocks.tsx +305 -0
  76. package/template/community/hamdiayari/akhbar/src/blocks/ConversionBlocks.tsx +569 -0
  77. package/template/community/hamdiayari/akhbar/src/blocks/HeaderBlocks.tsx +287 -0
  78. package/template/community/hamdiayari/akhbar/src/index.tsx +51 -0
  79. package/template/community/hamdiayari/akhbar/src/kit.tsx +357 -0
  80. package/template/community/hamdiayari/akhbar/src/site.ts +96 -0
  81. package/template/community/hamdiayari/akhbar/src/styles.css +566 -0
  82. package/template/community/hamdiayari/akhbar-actualites/README.md +28 -0
  83. package/template/community/hamdiayari/akhbar-actualites/screenshot.jpg +0 -0
  84. package/template/community/hamdiayari/akhbar-actualites/snapify.module.json +107 -0
  85. package/template/community/hamdiayari/akhbar-actualites/src/admin/AudiencePage.tsx +185 -0
  86. package/template/community/hamdiayari/akhbar-actualites/src/admin/index.tsx +11 -0
  87. package/template/community/hamdiayari/akhbar-actualites/src/public/Archive.tsx +142 -0
  88. package/template/community/hamdiayari/akhbar-actualites/src/public/cards.tsx +152 -0
  89. package/template/community/hamdiayari/akhbar-actualites/src/public/index.tsx +71 -0
  90. package/template/community/hamdiayari/akhbar-actualites/src/public/posts.ts +103 -0
  91. package/template/community/hamdiayari/akhbar-actualites/src/public/styles.css +568 -0
  92. package/template/community/hamdiayari/akhbar-actualites/src/public/widgets.tsx +226 -0
  93. package/template/community/hamdiayari/akhbar-actualites/src/server.ts +119 -0
  94. package/template/community/hamdiayari/akhbar-actualites/src/types.ts +25 -0
  95. package/template/community/hamdiayari/akhbar-urgent/README.md +36 -0
  96. package/template/community/hamdiayari/akhbar-urgent/screenshot.jpg +0 -0
  97. package/template/community/hamdiayari/akhbar-urgent/snapify.module.json +64 -0
  98. package/template/community/hamdiayari/akhbar-urgent/src/admin/FlashesPage.tsx +229 -0
  99. package/template/community/hamdiayari/akhbar-urgent/src/admin/index.tsx +11 -0
  100. package/template/community/hamdiayari/akhbar-urgent/src/public/Ticker.tsx +81 -0
  101. package/template/community/hamdiayari/akhbar-urgent/src/public/index.tsx +63 -0
  102. package/template/community/hamdiayari/akhbar-urgent/src/public/styles.css +142 -0
  103. package/template/community/hamdiayari/akhbar-urgent/src/server.ts +101 -0
  104. package/template/community/hamdiayari/akhbar-urgent/src/types.ts +35 -0
  105. package/template/community.html +21 -0
  106. package/template/developers.html +24 -0
  107. package/template/docker-compose.db.yml +24 -0
  108. package/template/docs/BACKOFFICE_SNAPIFY.md +130 -0
  109. package/template/docs/CI_CD.md +66 -0
  110. package/template/docs/MODULES.md +346 -0
  111. package/template/docs/PLATFORM_DEPLOY.md +54 -0
  112. package/template/docs/logo.svg +10 -0
  113. package/template/drizzle/0000_init.sql +147 -0
  114. package/template/drizzle/0001_page_project.sql +5 -0
  115. package/template/drizzle/0002_modules.sql +23 -0
  116. package/template/drizzle/0003_themes.sql +4 -0
  117. package/template/drizzle/0004_security.sql +82 -0
  118. package/template/drizzle/0005_module_records.sql +16 -0
  119. package/template/drizzle/0006_avatars.sql +4 -0
  120. package/template/drizzle/0007_platform.sql +264 -0
  121. package/template/drizzle/0008_mp_validation.sql +3 -0
  122. package/template/drizzle/0009_saas_ops.sql +77 -0
  123. package/template/drizzle/0010_cms_chrome.sql +13 -0
  124. package/template/drizzle/0011_client_portal.sql +26 -0
  125. package/template/drizzle/meta/0000_snapshot.json +998 -0
  126. package/template/drizzle/meta/0001_snapshot.json +1004 -0
  127. package/template/drizzle/meta/_journal.json +90 -0
  128. package/template/drizzle.config.ts +16 -0
  129. package/template/ecosystem.snapify.cjs +24 -0
  130. package/template/for.html +1 -0
  131. package/template/index.html +29 -0
  132. package/template/landing.html +37 -0
  133. package/template/marketplace/catalog.json +373 -0
  134. package/template/marketplace/gallery/hamdiayari.preveris/01-home.jpg +0 -0
  135. package/template/marketplace/gallery/hamdiayari.recaptcha/01-reglages.jpg +0 -0
  136. package/template/marketplace/gallery/hamdiayari.recaptcha/02-logo.jpg +0 -0
  137. package/template/marketplace/gallery/novalabs.ai-articles/01-generer.jpg +0 -0
  138. package/template/marketplace/gallery/novalabs.ai-articles/02-fournisseur-ia.jpg +0 -0
  139. package/template/marketplace/gallery/novalabs.calendar/01-agenda-site.jpg +0 -0
  140. package/template/marketplace/gallery/novalabs.calendar/02-evenements-admin.jpg +0 -0
  141. package/template/marketplace/gallery/novalabs.calendar/03-reglages.jpg +0 -0
  142. package/template/marketplace/screenshots/hamdiayari.preveris-1.0.6.jpg +0 -0
  143. package/template/marketplace/screenshots/hamdiayari.recaptcha-1.0.0.jpg +0 -0
  144. package/template/marketplace/screenshots/hamdiayari.wp-seo-migrator-1.1.1.jpg +0 -0
  145. package/template/marketplace/screenshots/novalabs.ai-articles-1.0.1.jpg +0 -0
  146. package/template/marketplace/screenshots/novalabs.ai-chat-1.0.1.jpg +0 -0
  147. package/template/marketplace/screenshots/novalabs.calendar-1.0.1.jpg +0 -0
  148. package/template/marketplace/screenshots/novalabs.cart-1.0.0.jpg +0 -0
  149. package/template/marketplace/screenshots/novalabs.checkout-1.0.0.jpg +0 -0
  150. package/template/marketplace/screenshots/novalabs.customers-1.1.0.jpg +0 -0
  151. package/template/marketplace/screenshots/novalabs.dogga-1.0.0.jpg +0 -0
  152. package/template/marketplace/screenshots/novalabs.mahdia-1.0.0.jpg +0 -0
  153. package/template/marketplace/screenshots/novalabs.shop-1.0.0.jpg +0 -0
  154. package/template/marketplace/screenshots/novalabs.sidibou-1.0.0.jpg +0 -0
  155. package/template/marketplace/screenshots/novalabs.whatsapp-1.0.1.jpg +0 -0
  156. package/template/marketplace.html +21 -0
  157. package/template/metadata.json +7 -0
  158. package/template/modules/ai-articles/screenshot.jpg +0 -0
  159. package/template/modules/ai-articles/snapify.module.json +74 -0
  160. package/template/modules/ai-articles/src/admin/ArticlesPage.tsx +162 -0
  161. package/template/modules/ai-articles/src/admin/GeneratePage.tsx +118 -0
  162. package/template/modules/ai-articles/src/admin/index.tsx +12 -0
  163. package/template/modules/ai-articles/src/public/index.tsx +119 -0
  164. package/template/modules/ai-articles/src/server.ts +287 -0
  165. package/template/modules/ai-articles/src/types.ts +30 -0
  166. package/template/modules/ai-chat/screenshot.jpg +0 -0
  167. package/template/modules/ai-chat/snapify.module.json +217 -0
  168. package/template/modules/ai-chat/src/public.tsx +198 -0
  169. package/template/modules/ai-chat/src/server.ts +142 -0
  170. package/template/modules/ai-theme-studio/README.md +15 -0
  171. package/template/modules/ai-theme-studio/screenshot.jpg +0 -0
  172. package/template/modules/ai-theme-studio/snapify.module.json +175 -0
  173. package/template/modules/ai-theme-studio/src/admin/HistoryPage.tsx +91 -0
  174. package/template/modules/ai-theme-studio/src/admin/StudioPage.tsx +414 -0
  175. package/template/modules/ai-theme-studio/src/admin/index.tsx +11 -0
  176. package/template/modules/ai-theme-studio/src/lib/images.ts +240 -0
  177. package/template/modules/ai-theme-studio/src/lib/llm.ts +123 -0
  178. package/template/modules/ai-theme-studio/src/lib/normalize.ts +714 -0
  179. package/template/modules/ai-theme-studio/src/lib/prompts.ts +198 -0
  180. package/template/modules/ai-theme-studio/src/server.ts +137 -0
  181. package/template/modules/ai-theme-studio/src/types.ts +112 -0
  182. package/template/modules/calendar/screenshot.jpg +0 -0
  183. package/template/modules/calendar/snapify.module.json +45 -0
  184. package/template/modules/calendar/src/admin/EventsPage.tsx +217 -0
  185. package/template/modules/calendar/src/admin/index.tsx +11 -0
  186. package/template/modules/calendar/src/public/index.tsx +231 -0
  187. package/template/modules/calendar/src/server.ts +143 -0
  188. package/template/modules/calendar/src/types.ts +25 -0
  189. package/template/modules/cart/screenshot.jpg +0 -0
  190. package/template/modules/cart/snapify.module.json +90 -0
  191. package/template/modules/cart/src/admin/index.tsx +265 -0
  192. package/template/modules/cart/src/public/CartPage.tsx +198 -0
  193. package/template/modules/cart/src/public/index.tsx +261 -0
  194. package/template/modules/cart/src/public/useQuote.ts +67 -0
  195. package/template/modules/cart/src/server.ts +148 -0
  196. package/template/modules/cart/src/types.ts +31 -0
  197. package/template/modules/checkout/screenshot.jpg +0 -0
  198. package/template/modules/checkout/snapify.module.json +264 -0
  199. package/template/modules/checkout/src/admin/OrderDetailPage.tsx +304 -0
  200. package/template/modules/checkout/src/admin/badges.tsx +28 -0
  201. package/template/modules/checkout/src/admin/index.tsx +236 -0
  202. package/template/modules/checkout/src/public/CheckoutPage.tsx +461 -0
  203. package/template/modules/checkout/src/public/OrderPages.tsx +153 -0
  204. package/template/modules/checkout/src/public/index.tsx +23 -0
  205. package/template/modules/checkout/src/server.ts +383 -0
  206. package/template/modules/checkout/src/types.ts +70 -0
  207. package/template/modules/customers/screenshot.jpg +0 -0
  208. package/template/modules/customers/snapify.module.json +115 -0
  209. package/template/modules/customers/src/admin/CustomerDetailPage.tsx +274 -0
  210. package/template/modules/customers/src/admin/index.tsx +194 -0
  211. package/template/modules/customers/src/public/AccountPages.tsx +470 -0
  212. package/template/modules/customers/src/public/AddressForm.tsx +115 -0
  213. package/template/modules/customers/src/public/AuthPages.tsx +349 -0
  214. package/template/modules/customers/src/public/Avatar.tsx +29 -0
  215. package/template/modules/customers/src/public/ProfileSection.tsx +291 -0
  216. package/template/modules/customers/src/public/index.tsx +137 -0
  217. package/template/modules/customers/src/public/session.ts +51 -0
  218. package/template/modules/customers/src/server.ts +421 -0
  219. package/template/modules/customers/src/types.ts +53 -0
  220. package/template/modules/recaptcha/logo.jpg +0 -0
  221. package/template/modules/recaptcha/screenshot.jpg +0 -0
  222. package/template/modules/recaptcha/snapify.module.json +99 -0
  223. package/template/modules/recaptcha/src/public/index.tsx +94 -0
  224. package/template/modules/recaptcha/src/server.ts +65 -0
  225. package/template/modules/recaptcha/src/types.ts +18 -0
  226. package/template/modules/shop/screenshot.jpg +0 -0
  227. package/template/modules/shop/snapify.module.json +340 -0
  228. package/template/modules/shop/src/admin/CategoriesPage.tsx +224 -0
  229. package/template/modules/shop/src/admin/ProductEditor.tsx +313 -0
  230. package/template/modules/shop/src/admin/ProductsPage.tsx +307 -0
  231. package/template/modules/shop/src/admin/index.tsx +14 -0
  232. package/template/modules/shop/src/public/Catalog.tsx +244 -0
  233. package/template/modules/shop/src/public/NotFound.tsx +22 -0
  234. package/template/modules/shop/src/public/ProductPage.tsx +195 -0
  235. package/template/modules/shop/src/public/index.tsx +134 -0
  236. package/template/modules/shop/src/public/useFetch.ts +26 -0
  237. package/template/modules/shop/src/server.ts +493 -0
  238. package/template/modules/shop/src/shared.ts +41 -0
  239. package/template/modules/shop/src/types.ts +60 -0
  240. package/template/modules/whatsapp/screenshot.jpg +0 -0
  241. package/template/modules/whatsapp/snapify.module.json +121 -0
  242. package/template/modules/whatsapp/src/public.tsx +119 -0
  243. package/template/modules/wp-seo-migrator/ARCHITECTURE.md +113 -0
  244. package/template/modules/wp-seo-migrator/README.md +21 -0
  245. package/template/modules/wp-seo-migrator/fixtures/aioseo-sample.csv +3 -0
  246. package/template/modules/wp-seo-migrator/fixtures/sample-exporter.json +39 -0
  247. package/template/modules/wp-seo-migrator/fixtures/sample-page.html +12 -0
  248. package/template/modules/wp-seo-migrator/fixtures/sample.htaccess +3 -0
  249. package/template/modules/wp-seo-migrator/screenshot.jpg +0 -0
  250. package/template/modules/wp-seo-migrator/snapify.module.json +87 -0
  251. package/template/modules/wp-seo-migrator/src/admin/HistoryPage.tsx +64 -0
  252. package/template/modules/wp-seo-migrator/src/admin/WizardPage.tsx +788 -0
  253. package/template/modules/wp-seo-migrator/src/admin/i18n.ts +156 -0
  254. package/template/modules/wp-seo-migrator/src/admin/index.tsx +11 -0
  255. package/template/modules/wp-seo-migrator/src/connectors/CUSTOM_CONNECTOR.md +31 -0
  256. package/template/modules/wp-seo-migrator/src/connectors/csvSeoFactory.ts +106 -0
  257. package/template/modules/wp-seo-migrator/src/connectors/exporter.ts +96 -0
  258. package/template/modules/wp-seo-migrator/src/connectors/gscCsv.ts +53 -0
  259. package/template/modules/wp-seo-migrator/src/connectors/htaccess.ts +65 -0
  260. package/template/modules/wp-seo-migrator/src/connectors/htmlCrawler.ts +90 -0
  261. package/template/modules/wp-seo-migrator/src/connectors/index.ts +113 -0
  262. package/template/modules/wp-seo-migrator/src/connectors/pluginCsv.ts +77 -0
  263. package/template/modules/wp-seo-migrator/src/connectors/rankMathCsv.ts +77 -0
  264. package/template/modules/wp-seo-migrator/src/connectors/redirection.ts +62 -0
  265. package/template/modules/wp-seo-migrator/src/connectors/sitemap.ts +44 -0
  266. package/template/modules/wp-seo-migrator/src/connectors/utils.ts +111 -0
  267. package/template/modules/wp-seo-migrator/src/connectors/wxr.ts +103 -0
  268. package/template/modules/wp-seo-migrator/src/connectors/yoastCsv.ts +56 -0
  269. package/template/modules/wp-seo-migrator/src/pipeline/analyze.ts +158 -0
  270. package/template/modules/wp-seo-migrator/src/pipeline/htmlParse.ts +58 -0
  271. package/template/modules/wp-seo-migrator/src/pipeline/remoteAuto.ts +328 -0
  272. package/template/modules/wp-seo-migrator/src/server.ts +492 -0
  273. package/template/modules/wp-seo-migrator/src/types.ts +151 -0
  274. package/template/modules/wp-seo-migrator/tests/connectors-extra.test.ts +39 -0
  275. package/template/modules/wp-seo-migrator/tests/merge.test.ts +25 -0
  276. package/template/package-lock.json +6016 -0
  277. package/template/package.json +74 -0
  278. package/template/public/images/photos/caravane-chameaux-desert.webp +0 -0
  279. package/template/public/images/photos/hammamet-medina-plage.jpg +0 -0
  280. package/template/public/images/photos/maison-campagne-collines.webp +0 -0
  281. package/template/public/images/photos/maisons-troglodytes-desert.jpg +0 -0
  282. package/template/public/images/photos/panorama-ville.jpg +0 -0
  283. package/template/public/images/photos/preveris-accessibilite-erp.jpg +0 -0
  284. package/template/public/images/photos/preveris-collectivites.jpg +0 -0
  285. package/template/public/images/photos/preveris-etudes-plans.jpg +0 -0
  286. package/template/public/images/photos/preveris-hero-erp.jpg +0 -0
  287. package/template/public/images/photos/preveris-ssi-incendie.jpg +0 -0
  288. package/template/public/images/photos/preveris-suivi-hotel.jpg +0 -0
  289. package/template/public/images/photos/ruelle-bleue-medina.jpg +0 -0
  290. package/template/public/images/photos/sidi-bou-said-port.webp +0 -0
  291. package/template/public/images/photos/terrasse-blanche-mer-voilier.jpg +0 -0
  292. package/template/public/landing/hero-ai-cms.jpg +0 -0
  293. package/template/public/landing/shot-dogga.jpg +0 -0
  294. package/template/public/landing/shot-mahdia.jpg +0 -0
  295. package/template/public/landing/shot-sidibou.jpg +0 -0
  296. package/template/public/llms.txt +35 -0
  297. package/template/public/logo.svg +10 -0
  298. package/template/scripts/app-pack.ts +155 -0
  299. package/template/scripts/app-publish.ts +100 -0
  300. package/template/scripts/apply-ecommerce-home.ts +196 -0
  301. package/template/scripts/auto-deploy-poll.sh +13 -0
  302. package/template/scripts/db-backup.sh +16 -0
  303. package/template/scripts/db-restore.sh +11 -0
  304. package/template/scripts/deploy.sh +34 -0
  305. package/template/scripts/github-webhook-deploy.sh +7 -0
  306. package/template/scripts/install-new-modules.ts +96 -0
  307. package/template/scripts/refresh-demo-baseline.ts +9 -0
  308. package/template/scripts/reinstall-marketplace-packs.ts +51 -0
  309. package/template/scripts/restore-classic-home.ts +13 -0
  310. package/template/scripts/sdk-pack.ts +289 -0
  311. package/template/scripts/sdk-publish.ts +75 -0
  312. package/template/scripts/seed-demo-products.ts +120 -0
  313. package/template/scripts/seed-theme-content-packs.ts +42 -0
  314. package/template/scripts/snapify-module.ts +207 -0
  315. package/template/scripts/test-theme-switch.ts +24 -0
  316. package/template/sdk/admin.css +18 -0
  317. package/template/sdk/admin.ts +95 -0
  318. package/template/sdk/build.ts +249 -0
  319. package/template/sdk/commerce.ts +512 -0
  320. package/template/sdk/content.ts +76 -0
  321. package/template/sdk/icons.ts +101 -0
  322. package/template/sdk/image.ts +60 -0
  323. package/template/sdk/index.ts +500 -0
  324. package/template/sdk/package.ts +298 -0
  325. package/template/sdk/recaptcha.tsx +122 -0
  326. package/template/sdk/settings.ts +84 -0
  327. package/template/sdk/theme.css +29 -0
  328. package/template/sdk/theme.ts +100 -0
  329. package/template/sdk/ui/commerce.tsx +385 -0
  330. package/template/sdk/ui/orders.tsx +136 -0
  331. package/template/sdk-kit/CHANGELOG.md +17 -0
  332. package/template/sdk-kit/LICENSE.md +47 -0
  333. package/template/sdk-kit/README.md +81 -0
  334. package/template/sdk-kit/cli.ts +339 -0
  335. package/template/sdk-kit/create-snapify/README.md +28 -0
  336. package/template/sdk-kit/create-snapify/create-snapify.mjs +11 -0
  337. package/template/sdk-kit/create-snapify/package.template.json +16 -0
  338. package/template/sdk-kit/package.template.json +95 -0
  339. package/template/sdk-kit/templates/common/README.md.tpl +27 -0
  340. package/template/sdk-kit/templates/common/_gitignore +3 -0
  341. package/template/sdk-kit/templates/common/package.json.tpl +19 -0
  342. package/template/sdk-kit/templates/common/src/snapify-env.d.ts +2 -0
  343. package/template/sdk-kit/templates/common/tsconfig.json +15 -0
  344. package/template/sdk-kit/templates/module/snapify.module.json.tpl +33 -0
  345. package/template/sdk-kit/templates/module/src/admin.tsx +53 -0
  346. package/template/sdk-kit/templates/module/src/public.tsx +49 -0
  347. package/template/sdk-kit/templates/module/src/server.ts +30 -0
  348. package/template/sdk-kit/templates/module/src/types.ts +19 -0
  349. package/template/sdk-kit/templates/theme/snapify.theme.json.tpl +22 -0
  350. package/template/sdk-kit/templates/theme/src/Footer.tsx +40 -0
  351. package/template/sdk-kit/templates/theme/src/Header.tsx +47 -0
  352. package/template/sdk-kit/templates/theme/src/Views.tsx +36 -0
  353. package/template/sdk-kit/templates/theme/src/blocks.tsx +93 -0
  354. package/template/sdk-kit/templates/theme/src/index.tsx +30 -0
  355. package/template/sdk-kit/templates/theme/src/site.tsx +59 -0
  356. package/template/sdk-kit/templates/theme/src/styles.css +71 -0
  357. package/template/server/client/routes.ts +252 -0
  358. package/template/server/cms/analyticsStore.ts +180 -0
  359. package/template/server/cms/auth.ts +155 -0
  360. package/template/server/cms/blockCatalog.ts +342 -0
  361. package/template/server/cms/blocks.ts +521 -0
  362. package/template/server/cms/contentMigration.ts +1119 -0
  363. package/template/server/cms/defaults.ts +303 -0
  364. package/template/server/cms/permissions.ts +154 -0
  365. package/template/server/cms/restoreClassicHome.ts +183 -0
  366. package/template/server/cms/revisions.ts +99 -0
  367. package/template/server/cms/scheduler.ts +78 -0
  368. package/template/server/cms/seed.ts +72 -0
  369. package/template/server/cms/seoAnalyzer.ts +213 -0
  370. package/template/server/cms/store.ts +122 -0
  371. package/template/server/cms/themeContent.ts +1386 -0
  372. package/template/server/cms/themePacks/sidibou-branding.json +1 -0
  373. package/template/server/cms/themePacks/sidibou-home.json +1 -0
  374. package/template/server/cms/themePacks/sidibou-menus.json +1 -0
  375. package/template/server/cms/types.ts +432 -0
  376. package/template/server/community/routes.ts +410 -0
  377. package/template/server/db/client.ts +43 -0
  378. package/template/server/db/repository.ts +389 -0
  379. package/template/server/db/schema.ts +726 -0
  380. package/template/server/demo/cmsReset.ts +196 -0
  381. package/template/server/developers/sdk.ts +85 -0
  382. package/template/server/geoip.ts +15 -0
  383. package/template/server/http.ts +53 -0
  384. package/template/server/mail.ts +216 -0
  385. package/template/server/marketplace/catalog.ts +74 -0
  386. package/template/server/marketplace/packageValidate.ts +475 -0
  387. package/template/server/marketplace/payments.ts +193 -0
  388. package/template/server/marketplace/routes.ts +850 -0
  389. package/template/server/marketplace/signing.ts +116 -0
  390. package/template/server/marketplace/syncOfficial.ts +282 -0
  391. package/template/server/media/images.ts +63 -0
  392. package/template/server/middleware/adminAuth.ts +134 -0
  393. package/template/server/modules/legacy.ts +44 -0
  394. package/template/server/modules/manager.ts +1122 -0
  395. package/template/server/modules/marketplace.ts +178 -0
  396. package/template/server/modules/outbound.ts +178 -0
  397. package/template/server/modules/purchases.ts +149 -0
  398. package/template/server/modules/records.ts +168 -0
  399. package/template/server/modules/themeHtml.ts +32 -0
  400. package/template/server/modules/trust.ts +16 -0
  401. package/template/server/modules/types.ts +147 -0
  402. package/template/server/platform/auth.ts +223 -0
  403. package/template/server/platform/clientWelcome.ts +168 -0
  404. package/template/server/platform/clients.ts +139 -0
  405. package/template/server/platform/dailyImprove.ts +356 -0
  406. package/template/server/platform/flouci.ts +126 -0
  407. package/template/server/platform/host.ts +69 -0
  408. package/template/server/platform/hostingProviders.ts +143 -0
  409. package/template/server/platform/licenseOrders.ts +291 -0
  410. package/template/server/platform/money.ts +42 -0
  411. package/template/server/platform/passwords.ts +41 -0
  412. package/template/server/platform/region.ts +86 -0
  413. package/template/server/platform/routes.ts +225 -0
  414. package/template/server/platform/seed.ts +82 -0
  415. package/template/server/platform/settings.ts +72 -0
  416. package/template/server/routes/adminAccount.ts +403 -0
  417. package/template/server/routes/adminCms.ts +808 -0
  418. package/template/server/routes/adminSystem.ts +243 -0
  419. package/template/server/routes/adminUsers.ts +449 -0
  420. package/template/server/routes/googleOAuth.ts +327 -0
  421. package/template/server/routes/modules.ts +323 -0
  422. package/template/server/routes/publicCms.ts +313 -0
  423. package/template/server/routes/seoPublic.ts +158 -0
  424. package/template/server/security/accounts.ts +121 -0
  425. package/template/server/security/audit.ts +143 -0
  426. package/template/server/security/authTokens.ts +74 -0
  427. package/template/server/security/ip.ts +68 -0
  428. package/template/server/security/rateLimit.ts +85 -0
  429. package/template/server/security/retention.ts +46 -0
  430. package/template/server/security/secrets.ts +44 -0
  431. package/template/server/security/sessions.ts +140 -0
  432. package/template/server/security/totp.ts +101 -0
  433. package/template/server/superadmin/analytics.ts +109 -0
  434. package/template/server/superadmin/auth.ts +92 -0
  435. package/template/server/superadmin/backlinksCatalog.ts +210 -0
  436. package/template/server/superadmin/frSeoContent.ts +165 -0
  437. package/template/server/superadmin/landingSeo.ts +675 -0
  438. package/template/server/superadmin/ops.ts +238 -0
  439. package/template/server/superadmin/routes.ts +1095 -0
  440. package/template/server/superadmin/tnSeoContent.ts +130 -0
  441. package/template/server.ts +522 -0
  442. package/template/src/App.tsx +263 -0
  443. package/template/src/admin/AdminApp.tsx +162 -0
  444. package/template/src/admin/account/AuthLayout.tsx +57 -0
  445. package/template/src/admin/account/SecurityForms.tsx +276 -0
  446. package/template/src/admin/admin.css +127 -0
  447. package/template/src/admin/context/AuthContext.tsx +147 -0
  448. package/template/src/admin/context/SiteContext.tsx +50 -0
  449. package/template/src/admin/editor/BlockLibrary.tsx +173 -0
  450. package/template/src/admin/editor/BlocksPanel.tsx +396 -0
  451. package/template/src/admin/editor/EditorBits.tsx +106 -0
  452. package/template/src/admin/editor/PreviewFrame.tsx +145 -0
  453. package/template/src/admin/editor/RichTextEditor.tsx +276 -0
  454. package/template/src/admin/editor/SeoPanel.tsx +349 -0
  455. package/template/src/admin/editor/StyleEditor.tsx +114 -0
  456. package/template/src/admin/editor/blockSchemas.ts +699 -0
  457. package/template/src/admin/editor/fields.tsx +486 -0
  458. package/template/src/admin/layout/AdminShell.tsx +418 -0
  459. package/template/src/admin/layout/nav.ts +95 -0
  460. package/template/src/admin/lib/api.ts +408 -0
  461. package/template/src/admin/lib/audit.ts +141 -0
  462. package/template/src/admin/lib/format.ts +84 -0
  463. package/template/src/admin/lib/hooks.ts +125 -0
  464. package/template/src/admin/main.tsx +16 -0
  465. package/template/src/admin/modules/InstallDialog.tsx +160 -0
  466. package/template/src/admin/modules/SettingsForm.tsx +145 -0
  467. package/template/src/admin/modules/kit.tsx +127 -0
  468. package/template/src/admin/modules/labels.tsx +226 -0
  469. package/template/src/admin/pages/AccountSetupPage.tsx +62 -0
  470. package/template/src/admin/pages/AnalyticsPage.tsx +206 -0
  471. package/template/src/admin/pages/BrandingPage.tsx +154 -0
  472. package/template/src/admin/pages/DashboardPage.tsx +239 -0
  473. package/template/src/admin/pages/EngagementPage.tsx +168 -0
  474. package/template/src/admin/pages/FormsPage.tsx +435 -0
  475. package/template/src/admin/pages/GeneralPage.tsx +120 -0
  476. package/template/src/admin/pages/LoginPage.tsx +249 -0
  477. package/template/src/admin/pages/MaintenancePage.tsx +125 -0
  478. package/template/src/admin/pages/MarketplacePage.tsx +426 -0
  479. package/template/src/admin/pages/MediaPage.tsx +251 -0
  480. package/template/src/admin/pages/MenusPage.tsx +446 -0
  481. package/template/src/admin/pages/ModuleAdminPage.tsx +113 -0
  482. package/template/src/admin/pages/ModuleDetailPage.tsx +293 -0
  483. package/template/src/admin/pages/ModulesPage.tsx +135 -0
  484. package/template/src/admin/pages/NewsletterPage.tsx +193 -0
  485. package/template/src/admin/pages/PageEditorPage.tsx +322 -0
  486. package/template/src/admin/pages/PagesListPage.tsx +277 -0
  487. package/template/src/admin/pages/PostEditorPage.tsx +292 -0
  488. package/template/src/admin/pages/PostsListPage.tsx +187 -0
  489. package/template/src/admin/pages/ProfilePage.tsx +489 -0
  490. package/template/src/admin/pages/ResetPasswordPage.tsx +110 -0
  491. package/template/src/admin/pages/SeoPage.tsx +670 -0
  492. package/template/src/admin/pages/SettingsPage.tsx +189 -0
  493. package/template/src/admin/pages/ThemeCustomizePage.tsx +170 -0
  494. package/template/src/admin/pages/ThemesPage.tsx +292 -0
  495. package/template/src/admin/pages/UsersPage.tsx +57 -0
  496. package/template/src/admin/pages/WidgetsPage.tsx +200 -0
  497. package/template/src/admin/seo/GoogleSetup.tsx +529 -0
  498. package/template/src/admin/settings/EmailTab.tsx +293 -0
  499. package/template/src/admin/settings/OtherTabs.tsx +270 -0
  500. package/template/src/admin/settings/SecurityTab.tsx +172 -0
  501. package/template/src/admin/settings/controls.tsx +133 -0
  502. package/template/src/admin/ui/Pagination.tsx +170 -0
  503. package/template/src/admin/ui/SaveBar.tsx +77 -0
  504. package/template/src/admin/ui/TagInput.tsx +54 -0
  505. package/template/src/admin/ui/charts.tsx +108 -0
  506. package/template/src/admin/ui/media.tsx +265 -0
  507. package/template/src/admin/ui/overlays.tsx +239 -0
  508. package/template/src/admin/ui/primitives.tsx +544 -0
  509. package/template/src/admin/users/AuditTab.tsx +104 -0
  510. package/template/src/admin/users/RolesTab.tsx +368 -0
  511. package/template/src/admin/users/UsersTab.tsx +723 -0
  512. package/template/src/admin/users/shared.tsx +278 -0
  513. package/template/src/cms/icons.ts +7 -0
  514. package/template/src/components/CookieBanner.tsx +166 -0
  515. package/template/src/components/MaintenanceGate.tsx +66 -0
  516. package/template/src/components/SiteAnalytics.tsx +64 -0
  517. package/template/src/components/SnapifyLogo.tsx +44 -0
  518. package/template/src/components/cms/BlockRenderer.tsx +154 -0
  519. package/template/src/components/cms/WidgetArea.tsx +193 -0
  520. package/template/src/context/SiteConfigContext.tsx +130 -0
  521. package/template/src/demoBase.ts +23 -0
  522. package/template/src/index.css +90 -0
  523. package/template/src/main.tsx +16 -0
  524. package/template/src/modules/ModuleHost.tsx +197 -0
  525. package/template/src/preview.ts +63 -0
  526. package/template/src/runtime.ts +38 -0
  527. package/template/src/themes/ThemeHost.tsx +168 -0
  528. package/template/src/themes/fallback.tsx +71 -0
  529. package/template/src/types.ts +94 -0
  530. package/template/src/views/BlogPostView.tsx +68 -0
  531. package/template/src/views/CmsPageView.tsx +34 -0
  532. package/template/start-pm2.sh +3 -0
  533. package/template/superadmin.html +20 -0
  534. package/template/themes/ai-canvas/screenshot.jpg +0 -0
  535. package/template/themes/ai-canvas/snapify.theme.json +96 -0
  536. package/template/themes/ai-canvas/src/CeeSimulator.tsx +247 -0
  537. package/template/themes/ai-canvas/src/Footer.tsx +190 -0
  538. package/template/themes/ai-canvas/src/Header.tsx +324 -0
  539. package/template/themes/ai-canvas/src/Layout.tsx +56 -0
  540. package/template/themes/ai-canvas/src/NotFound.tsx +58 -0
  541. package/template/themes/ai-canvas/src/Post.tsx +26 -0
  542. package/template/themes/ai-canvas/src/SnapifyLogo.tsx +44 -0
  543. package/template/themes/ai-canvas/src/blocks/CollectionBlocks.tsx +711 -0
  544. package/template/themes/ai-canvas/src/blocks/ContentBlocks.tsx +318 -0
  545. package/template/themes/ai-canvas/src/blocks/ConversionBlocks.tsx +559 -0
  546. package/template/themes/ai-canvas/src/blocks/HeaderBlocks.tsx +341 -0
  547. package/template/themes/ai-canvas/src/index.tsx +54 -0
  548. package/template/themes/ai-canvas/src/kit.tsx +398 -0
  549. package/template/themes/ai-canvas/src/site.ts +29 -0
  550. package/template/themes/ai-canvas/src/styles.css +317 -0
  551. package/template/themes/dogga/screenshot.jpg +0 -0
  552. package/template/themes/dogga/snapify.theme.json +55 -0
  553. package/template/themes/dogga/src/Brand.tsx +41 -0
  554. package/template/themes/dogga/src/Footer.tsx +202 -0
  555. package/template/themes/dogga/src/Header.tsx +239 -0
  556. package/template/themes/dogga/src/Layout.tsx +43 -0
  557. package/template/themes/dogga/src/Views.tsx +71 -0
  558. package/template/themes/dogga/src/blocks/CollectionBlocks.tsx +674 -0
  559. package/template/themes/dogga/src/blocks/ContentBlocks.tsx +307 -0
  560. package/template/themes/dogga/src/blocks/ConversionBlocks.tsx +571 -0
  561. package/template/themes/dogga/src/blocks/HeaderBlocks.tsx +289 -0
  562. package/template/themes/dogga/src/index.tsx +53 -0
  563. package/template/themes/dogga/src/kit.tsx +362 -0
  564. package/template/themes/dogga/src/site.ts +56 -0
  565. package/template/themes/dogga/src/styles.css +396 -0
  566. package/template/themes/mahdia/screenshot.jpg +0 -0
  567. package/template/themes/mahdia/snapify.theme.json +65 -0
  568. package/template/themes/mahdia/src/Brand.tsx +39 -0
  569. package/template/themes/mahdia/src/Footer.tsx +210 -0
  570. package/template/themes/mahdia/src/Header.tsx +291 -0
  571. package/template/themes/mahdia/src/Layout.tsx +44 -0
  572. package/template/themes/mahdia/src/Views.tsx +79 -0
  573. package/template/themes/mahdia/src/blocks/CollectionBlocks.tsx +674 -0
  574. package/template/themes/mahdia/src/blocks/ContentBlocks.tsx +307 -0
  575. package/template/themes/mahdia/src/blocks/ConversionBlocks.tsx +571 -0
  576. package/template/themes/mahdia/src/blocks/HeaderBlocks.tsx +289 -0
  577. package/template/themes/mahdia/src/index.tsx +53 -0
  578. package/template/themes/mahdia/src/kit.tsx +362 -0
  579. package/template/themes/mahdia/src/site.ts +75 -0
  580. package/template/themes/mahdia/src/styles.css +446 -0
  581. package/template/themes/preveris/screenshot.jpg +0 -0
  582. package/template/themes/preveris/snapify.theme.json +126 -0
  583. package/template/themes/preveris/src/Brand.tsx +44 -0
  584. package/template/themes/preveris/src/Footer.tsx +202 -0
  585. package/template/themes/preveris/src/Header.tsx +239 -0
  586. package/template/themes/preveris/src/Layout.tsx +42 -0
  587. package/template/themes/preveris/src/Views.tsx +71 -0
  588. package/template/themes/preveris/src/blocks/CollectionBlocks.tsx +678 -0
  589. package/template/themes/preveris/src/blocks/ContentBlocks.tsx +307 -0
  590. package/template/themes/preveris/src/blocks/ConversionBlocks.tsx +571 -0
  591. package/template/themes/preveris/src/blocks/HeaderBlocks.tsx +299 -0
  592. package/template/themes/preveris/src/index.tsx +53 -0
  593. package/template/themes/preveris/src/kit.tsx +365 -0
  594. package/template/themes/preveris/src/site.ts +79 -0
  595. package/template/themes/preveris/src/styles.css +446 -0
  596. package/template/themes/sidibou/screenshot.jpg +0 -0
  597. package/template/themes/sidibou/snapify.theme.json +21 -0
  598. package/template/themes/sidibou/src/CeeSimulator.tsx +247 -0
  599. package/template/themes/sidibou/src/Footer.tsx +203 -0
  600. package/template/themes/sidibou/src/Header.tsx +324 -0
  601. package/template/themes/sidibou/src/Layout.tsx +53 -0
  602. package/template/themes/sidibou/src/NotFound.tsx +58 -0
  603. package/template/themes/sidibou/src/Post.tsx +26 -0
  604. package/template/themes/sidibou/src/SnapifyLogo.tsx +44 -0
  605. package/template/themes/sidibou/src/blocks/CollectionBlocks.tsx +711 -0
  606. package/template/themes/sidibou/src/blocks/ContentBlocks.tsx +318 -0
  607. package/template/themes/sidibou/src/blocks/ConversionBlocks.tsx +559 -0
  608. package/template/themes/sidibou/src/blocks/HeaderBlocks.tsx +341 -0
  609. package/template/themes/sidibou/src/index.tsx +54 -0
  610. package/template/themes/sidibou/src/kit.tsx +398 -0
  611. package/template/themes/sidibou/src/site.ts +28 -0
  612. package/template/themes/sidibou/src/styles.css +317 -0
  613. package/template/tsconfig.json +33 -0
  614. package/template/vite.config.ts +42 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Snapify
3
+ * © 2026 NOVALABS WEB DESIGN — Tunisie
4
+ * Contact : contact@novalabs.tn · +216 24 734 647
5
+ * Tous droits réservés. Toute reproduction, modification ou diffusion sans autorisation écrite est interdite.
6
+ */
7
+ import React, { useEffect, useState } from 'react';
8
+ import type { BlogPost } from '../../server/cms/types';
9
+ import { BlockRenderer } from '../components/cms/BlockRenderer';
10
+ import { useActiveTheme } from '../themes/ThemeHost';
11
+
12
+ export function BlogPostView({
13
+ slug,
14
+ onNavigate,
15
+ previewPost,
16
+ selectedBlockId,
17
+ }: {
18
+ slug: string;
19
+ onNavigate: (path: string) => void;
20
+ previewPost?: BlogPost | null;
21
+ selectedBlockId?: string | null;
22
+ }) {
23
+ const [post, setPost] = useState<BlogPost | null>(previewPost ?? null);
24
+ const [error, setError] = useState(false);
25
+ const { theme } = useActiveTheme();
26
+
27
+ useEffect(() => {
28
+ if (previewPost) {
29
+ setPost(previewPost);
30
+ setError(false);
31
+ return;
32
+ }
33
+ let alive = true;
34
+ setPost(null);
35
+ setError(false);
36
+ fetch(`/api/public/posts/${slug}`)
37
+ .then((r) => {
38
+ if (!r.ok) throw new Error('404');
39
+ return r.json();
40
+ })
41
+ .then((p) => alive && setPost(p))
42
+ .catch(() => alive && setError(true));
43
+ return () => {
44
+ alive = false;
45
+ };
46
+ }, [slug, previewPost]);
47
+
48
+ if (error) {
49
+ return (
50
+ <div className="py-20 text-center">
51
+ <p>Article introuvable.</p>
52
+ <button type="button" className="mt-4 text-px-primary-dark underline" onClick={() => onNavigate('/')}>
53
+ Accueil
54
+ </button>
55
+ </div>
56
+ );
57
+ }
58
+ if (!post) return <div className="py-20 text-center text-slate-500">Chargement…</div>;
59
+
60
+ const blocks = <BlockRenderer blocks={post.blocks} selectedId={selectedBlockId} />;
61
+ if (theme.Post) return <theme.Post post={post}>{blocks}</theme.Post>;
62
+ return (
63
+ <article className="mx-auto max-w-3xl px-4 py-10">
64
+ <h1 className="mb-6 text-3xl font-bold">{post.title}</h1>
65
+ {blocks}
66
+ </article>
67
+ );
68
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Snapify
3
+ * © 2026 NOVALABS WEB DESIGN — Tunisie
4
+ * Contact : contact@novalabs.tn · +216 24 734 647
5
+ * Tous droits réservés. Toute reproduction, modification ou diffusion sans autorisation écrite est interdite.
6
+ */
7
+ import React, { useEffect } from 'react';
8
+ import type { CmsPage } from '../../server/cms/types';
9
+ import { BlockRenderer } from '../components/cms/BlockRenderer';
10
+ import { useActiveTheme } from '../themes/ThemeHost';
11
+
12
+ export function CmsPageView({
13
+ page,
14
+ selectedBlockId,
15
+ scrollToBlockId,
16
+ scrollNonce,
17
+ }: {
18
+ page: CmsPage;
19
+ selectedBlockId?: string | null;
20
+ scrollToBlockId?: string | null;
21
+ scrollNonce?: number;
22
+ }) {
23
+ useEffect(() => {
24
+ if (!scrollToBlockId) return;
25
+ const el = document.getElementById(`px-block-${scrollToBlockId}`);
26
+ if (!el) return;
27
+ const top = el.getBoundingClientRect().top + window.scrollY - 90;
28
+ window.scrollTo({ top: Math.max(top, 0), behavior: 'smooth' });
29
+ }, [scrollToBlockId, scrollNonce]);
30
+
31
+ const { theme } = useActiveTheme();
32
+ const blocks = <BlockRenderer blocks={page.blocks} selectedId={selectedBlockId} />;
33
+ return theme.Page ? <theme.Page page={page}>{blocks}</theme.Page> : blocks;
34
+ }
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+ cd /home/snapify
3
+ exec /usr/bin/npx tsx server.ts
@@ -0,0 +1,20 @@
1
+ <!doctype html>
2
+ <!--
3
+ Snapify Superadmin
4
+ © 2026 NOVALABS WEB DESIGN — Tunisie
5
+ -->
6
+ <html lang="fr">
7
+ <head>
8
+ <meta charset="UTF-8" />
9
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
10
+ <meta name="robots" content="noindex,nofollow" />
11
+ <title>Snapify Superadmin</title>
12
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
13
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
14
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
15
+ </head>
16
+ <body class="bg-slate-50 text-slate-900 antialiased">
17
+ <div id="root"></div>
18
+ <script type="module" src="/apps/superadmin/src/main.tsx"></script>
19
+ </body>
20
+ </html>
@@ -0,0 +1,96 @@
1
+ {
2
+ "id": "hamdiayari.ai-canvas",
3
+ "name": "AI Canvas",
4
+ "version": "1.0.1",
5
+ "description": "Thème paramétrique conçu pour l’atelier IA : les 25 blocs du constructeur, identité visuelle Snapify, en-tête e-commerce (header-actions). À activer après une génération dans « Atelier thème IA ».",
6
+ "author": {
7
+ "name": "Hamdi Ayari",
8
+ "email": "hamdi.ayari@novalabs.tn",
9
+ "url": "https://novalabs.tn"
10
+ },
11
+ "pricing": "free",
12
+ "category": "Polyvalent",
13
+ "engines": {
14
+ "snapify": "^1.3.0"
15
+ },
16
+ "entries": {
17
+ "public": "src/index.tsx"
18
+ },
19
+ "screenshot": "screenshot.jpg",
20
+ "settings": [
21
+ {
22
+ "key": "stickyHeader",
23
+ "type": "boolean",
24
+ "group": "En-tête",
25
+ "label": "En-tête fixe",
26
+ "help": "L’en-tête reste visible pendant le défilement.",
27
+ "default": true
28
+ },
29
+ {
30
+ "key": "headerPhone",
31
+ "type": "boolean",
32
+ "group": "En-tête",
33
+ "label": "Afficher le téléphone",
34
+ "help": "Numéro cliquable à côté du bouton principal (ordinateur).",
35
+ "default": true
36
+ },
37
+ {
38
+ "key": "buttonShape",
39
+ "type": "select",
40
+ "group": "Style",
41
+ "label": "Forme des boutons",
42
+ "default": "pill",
43
+ "options": [
44
+ { "value": "pill", "label": "Arrondis (pilule)" },
45
+ { "value": "rounded", "label": "Coins adoucis" },
46
+ { "value": "square", "label": "Carrés" }
47
+ ]
48
+ },
49
+ {
50
+ "key": "cardRadius",
51
+ "type": "number",
52
+ "group": "Style",
53
+ "label": "Arrondi des cartes (px)",
54
+ "default": 20,
55
+ "min": 0,
56
+ "max": 32,
57
+ "step": 2,
58
+ "slider": true
59
+ },
60
+ {
61
+ "key": "density",
62
+ "type": "select",
63
+ "group": "Style",
64
+ "label": "Densité",
65
+ "default": "comfortable",
66
+ "options": [
67
+ { "value": "compact", "label": "Compacte" },
68
+ { "value": "comfortable", "label": "Confortable" },
69
+ { "value": "airy", "label": "Aérée" }
70
+ ]
71
+ },
72
+ {
73
+ "key": "animations",
74
+ "type": "boolean",
75
+ "group": "Style",
76
+ "label": "Animations",
77
+ "help": "Apparition des sections et effets décoratifs.",
78
+ "default": true
79
+ },
80
+ {
81
+ "key": "mobileBar",
82
+ "type": "boolean",
83
+ "group": "Mobile",
84
+ "label": "Barre d’actions mobile",
85
+ "help": "Boutons fixés en bas de l’écran sur mobile.",
86
+ "default": true
87
+ },
88
+ {
89
+ "key": "backToTop",
90
+ "type": "boolean",
91
+ "group": "Navigation",
92
+ "label": "Bouton « Revenir en haut »",
93
+ "default": false
94
+ }
95
+ ]
96
+ }
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Snapify
3
+ * © 2026 NOVALABS WEB DESIGN — Tunisie
4
+ * Contact : contact@novalabs.tn · +216 24 734 647
5
+ * Tous droits réservés. Toute reproduction, modification ou diffusion sans autorisation écrite est interdite.
6
+ */
7
+ import React, { useState } from 'react';
8
+ import { Calculator, ArrowRight, CheckCircle2, ShieldCheck, Zap } from 'lucide-react';
9
+
10
+ interface SimulatorProps {
11
+ onSelectSolution?: (data: { siteType: string; need: string; estimate: string }) => void;
12
+ onNavigateToContact?: () => void;
13
+ }
14
+
15
+ export const CeeSimulator: React.FC<SimulatorProps> = ({ onNavigateToContact }) => {
16
+ const [siteType, setSiteType] = useState<'tertiaire' | 'industrie' | 'residentiel'>('tertiaire');
17
+ const [surfaceM2, setSurfaceM2] = useState<number>(2500);
18
+ const [targetSolution, setTargetSolution] = useState<string>('froid');
19
+
20
+ // Realistic French CEE and energy engineering heuristics
21
+ const calculateEstimates = () => {
22
+ let kwhCumacPerUnit = 420;
23
+ let baseCoverage = 0.72; // 72%
24
+ let typicalSavingPercent = 22;
25
+
26
+ if (siteType === 'industrie') {
27
+ if (targetSolution === 'recup-chaleur') {
28
+ kwhCumacPerUnit = 850;
29
+ baseCoverage = 0.78;
30
+ typicalSavingPercent = 28;
31
+ } else if (targetSolution === 'froid') {
32
+ kwhCumacPerUnit = 680;
33
+ baseCoverage = 0.75;
34
+ typicalSavingPercent = 24;
35
+ } else {
36
+ kwhCumacPerUnit = 520;
37
+ baseCoverage = 0.70;
38
+ typicalSavingPercent = 18;
39
+ }
40
+ } else if (siteType === 'tertiaire') {
41
+ if (targetSolution === 'froid') {
42
+ kwhCumacPerUnit = 600;
43
+ baseCoverage = 0.76;
44
+ typicalSavingPercent = 25;
45
+ } else if (targetSolution === 'gtb') {
46
+ kwhCumacPerUnit = 380;
47
+ baseCoverage = 0.68;
48
+ typicalSavingPercent = 20;
49
+ } else if (targetSolution === 'destratification') {
50
+ kwhCumacPerUnit = 310;
51
+ baseCoverage = 0.65;
52
+ typicalSavingPercent = 22;
53
+ }
54
+ } else {
55
+ // Résidentiel collectif
56
+ kwhCumacPerUnit = 390;
57
+ baseCoverage = 0.74;
58
+ typicalSavingPercent = 26;
59
+ }
60
+
61
+ const estimatedKwhCumac = Math.round((surfaceM2 * kwhCumacPerUnit) / 1000) * 1000;
62
+ const estimatedSavingsPercent = typicalSavingPercent;
63
+ const estimatedCeeCoverage = Math.round(baseCoverage * 100);
64
+
65
+ return {
66
+ kwhCumac: estimatedKwhCumac.toLocaleString('fr-FR'),
67
+ savingsPercent: estimatedSavingsPercent,
68
+ ceeCoverage: estimatedCeeCoverage
69
+ };
70
+ };
71
+
72
+ const results = calculateEstimates();
73
+
74
+ return (
75
+ <div className="card-snapify p-6 sm:p-8 lg:p-10 shadow-sm border border-px-border">
76
+ <div className="flex flex-col md:flex-row md:items-center justify-between gap-4 pb-6 border-b border-px-border/70">
77
+ <div>
78
+ <div className="flex items-center gap-2 text-xs font-semibold uppercase tracking-wider text-px-primary-dark mb-1">
79
+ <Calculator className="w-4 h-4 text-px-primary" />
80
+ <span>Eu nibh & Euismod</span>
81
+ </div>
82
+ <h3 className="text-xl sm:text-2xl font-semibold text-px-navy">
83
+ Gravida duis AC & Tellus et
84
+ </h3>
85
+ <p className="text-sm text-slate-500 mt-1">
86
+ Risus vulputate vehicula donec lobortis risus a ELIT etiam tempor lorem ipsum dolor sit amet consectetur adipiscing.
87
+ </p>
88
+ </div>
89
+ <div className="inline-flex items-center gap-1.5 self-start text-xs font-medium text-slate-500 bg-px-surface px-3 py-1.5 rounded-full border border-slate-200">
90
+ <ShieldCheck className="w-4 h-4 text-emerald-600" />
91
+ <span>Elit SED do Eiusmod 2026</span>
92
+ </div>
93
+ </div>
94
+
95
+ <div className="grid grid-cols-1 lg:grid-cols-12 gap-8 pt-8">
96
+ {/* Controls */}
97
+ <div className="lg:col-span-7 space-y-6">
98
+ {/* Site type */}
99
+ <div>
100
+ <label className="block text-xs font-semibold uppercase tracking-wider text-slate-500 mb-2">
101
+ 1. Tempor incididunt ut labore
102
+ </label>
103
+ <div className="grid grid-cols-3 gap-2 p-1 bg-px-surface rounded-xl border border-slate-200">
104
+ <button
105
+ type="button"
106
+ onClick={() => setSiteType('tertiaire')}
107
+ className={`py-2 px-3 text-xs sm:text-sm font-medium rounded-lg transition-all ${
108
+ siteType === 'tertiaire'
109
+ ? 'bg-white text-px-navy shadow-sm font-semibold'
110
+ : 'text-slate-600 hover:text-px-navy'
111
+ }`}
112
+ >
113
+ Et / Dolore
114
+ </button>
115
+ <button
116
+ type="button"
117
+ onClick={() => setSiteType('industrie')}
118
+ className={`py-2 px-3 text-xs sm:text-sm font-medium rounded-lg transition-all ${
119
+ siteType === 'industrie'
120
+ ? 'bg-white text-px-navy shadow-sm font-semibold'
121
+ : 'text-slate-600 hover:text-px-navy'
122
+ }`}
123
+ >
124
+ Magna Aliqua
125
+ </button>
126
+ <button
127
+ type="button"
128
+ onClick={() => setSiteType('residentiel')}
129
+ className={`py-2 px-3 text-xs sm:text-sm font-medium rounded-lg transition-all ${
130
+ siteType === 'residentiel'
131
+ ? 'bg-white text-px-navy shadow-sm font-semibold'
132
+ : 'text-slate-600 hover:text-px-navy'
133
+ }`}
134
+ >
135
+ Enim Ad
136
+ </button>
137
+ </div>
138
+ </div>
139
+
140
+ {/* Solution target */}
141
+ <div>
142
+ <label className="block text-xs font-semibold uppercase tracking-wider text-slate-500 mb-2">
143
+ 2. Minim veniam quis
144
+ </label>
145
+ <div className="grid grid-cols-2 sm:grid-cols-3 gap-2.5">
146
+ {[
147
+ { id: 'froid', label: 'Nostrud & EXERCITATION Ullamco', badge: 'LABORIS-NISI-134' },
148
+ { id: 'recup-chaleur', label: 'Aliquip Ex', badge: 'EA-COMMODO-103' },
149
+ { id: 'gtb', label: 'CONSEQUAT / Duis AUTE', badge: 'IRURE-IN-116' },
150
+ { id: 'destratification', label: 'Reprehenderit', badge: 'VOLUPTATE-VELIT-142' },
151
+ { id: 'isolation', label: 'Esse & Cillum', badge: 'FUGIAT-NULLA-106' },
152
+ { id: 'pac', label: 'Pariatur excepteur sint (OCCAECAT)', badge: 'CUPIDATAT-NON-163' }
153
+ ].map((sol) => (
154
+ <button
155
+ key={sol.id}
156
+ type="button"
157
+ onClick={() => setTargetSolution(sol.id)}
158
+ className={`text-left p-3 rounded-xl border text-xs sm:text-sm transition-all ${
159
+ targetSolution === sol.id
160
+ ? 'border-px-primary bg-px-primary/5 text-px-navy font-semibold ring-1 ring-px-primary'
161
+ : 'border-slate-200 bg-white hover:border-slate-300 text-slate-700'
162
+ }`}
163
+ >
164
+ <div className="truncate">{sol.label}</div>
165
+ <div className="text-[10px] text-slate-400 mt-1 font-mono">{sol.badge}</div>
166
+ </button>
167
+ ))}
168
+ </div>
169
+ </div>
170
+
171
+ {/* Surface slider */}
172
+ <div>
173
+ <div className="flex justify-between items-center text-xs font-semibold uppercase tracking-wider text-slate-500 mb-2">
174
+ <span>3. Proident sunt culpa qui officia</span>
175
+ <span className="text-px-navy font-mono text-sm lowercase font-bold">{surfaceM2.toLocaleString('fr-FR')} m²</span>
176
+ </div>
177
+ <input
178
+ type="range"
179
+ min={500}
180
+ max={25000}
181
+ step={250}
182
+ value={surfaceM2}
183
+ onChange={(e) => setSurfaceM2(Number(e.target.value))}
184
+ className="w-full accent-px-primary bg-slate-200 h-2 rounded-lg cursor-pointer"
185
+ />
186
+ <div className="flex justify-between text-[11px] text-slate-400 mt-1">
187
+ <span>500 m² (Mollit / Anim)</span>
188
+ <span>10 000 m² (Est / Laborum)</span>
189
+ <span>25 000 m² (Pretium Tincidunt)</span>
190
+ </div>
191
+ </div>
192
+ </div>
193
+
194
+ {/* Results Card */}
195
+ <div className="lg:col-span-5 bg-gradient-to-br from-px-navy to-px-navy-2 rounded-2xl p-6 sm:p-7 text-white flex flex-col justify-between shadow-xl">
196
+ <div>
197
+ <div className="flex items-center gap-2 text-xs font-semibold text-px-primary-light uppercase tracking-wider mb-4">
198
+ <Zap className="w-4 h-4 text-px-primary-light" />
199
+ <span>Lacus nulla Gravida</span>
200
+ </div>
201
+
202
+ <div className="space-y-5">
203
+ <div className="border-b border-white/10 pb-4">
204
+ <div className="text-xs text-slate-300">Orci A odio nullam</div>
205
+ <div className="text-2xl sm:text-3xl font-bold font-mono tracking-tight text-white mt-0.5">
206
+ ~ {results.kwhCumac} <span className="text-sm font-normal text-slate-400">varius turpis</span>
207
+ </div>
208
+ <div className="text-[11px] text-slate-400 mt-0.5">Et commodo pharetra est eros Snapify</div>
209
+ </div>
210
+
211
+ <div className="border-b border-white/10 pb-4">
212
+ <div className="text-xs text-slate-300">Bibendum elit nec luctus MAGNA</div>
213
+ <div className="text-2xl sm:text-3xl font-bold text-px-primary-light mt-0.5">
214
+ Jusqu'à {results.ceeCoverage} %
215
+ </div>
216
+ <div className="text-[11px] text-slate-400 mt-0.5">Felis sollicitudin mauris integer in</div>
217
+ </div>
218
+
219
+ <div>
220
+ <div className="text-xs text-slate-300">Mauris eu nibh euismod</div>
221
+ <div className="text-2xl sm:text-3xl font-bold text-white mt-0.5">
222
+ - {results.savingsPercent} %
223
+ </div>
224
+ <div className="text-[11px] text-slate-400 mt-0.5">Gravida duis ac tellus et</div>
225
+ </div>
226
+ </div>
227
+ </div>
228
+
229
+ <div className="pt-6 mt-6 border-t border-white/10 space-y-3">
230
+ <div className="flex items-center gap-2 text-xs text-slate-300">
231
+ <CheckCircle2 className="w-4 h-4 text-px-primary-light shrink-0" />
232
+ <span>Risus vulputate vehicula donec lobortis risus A</span>
233
+ </div>
234
+
235
+ <button
236
+ onClick={onNavigateToContact}
237
+ className="w-full btn-primary-snapify !py-3 text-center !w-full justify-center group"
238
+ >
239
+ <span>Elit etiam tempor lorem</span>
240
+ <ArrowRight className="w-4 h-4 btn-arrow" />
241
+ </button>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ </div>
246
+ );
247
+ };
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Snapify
3
+ * © 2026 NOVALABS WEB DESIGN — Tunisie
4
+ * Contact : contact@novalabs.tn · +216 24 734 647
5
+ * Tous droits réservés. Toute reproduction, modification ou diffusion sans autorisation écrite est interdite.
6
+ */
7
+ import React from 'react';
8
+ import { Phone, ArrowUpRight, ShieldCheck, CheckCircle } from 'lucide-react';
9
+ import { useSiteInfo, useSiteConfig } from './site';
10
+ import { SnapifyLogo } from './SnapifyLogo';
11
+
12
+ interface FooterProps {
13
+ onNavigate: (path: string) => void;
14
+ onOpenCookies: () => void;
15
+ }
16
+
17
+ const LEGAL = ['/mentions-legales/', '/politique-de-confidentialite/', '/cookies/'];
18
+
19
+ export const Footer: React.FC<FooterProps> = ({ onNavigate, onOpenCookies }) => {
20
+ const SITE_INFO = useSiteInfo();
21
+ const siteConfig = useSiteConfig();
22
+ const [newsEmail, setNewsEmail] = React.useState('');
23
+ const [newsMsg, setNewsMsg] = React.useState('');
24
+
25
+ const navigateTo = (path: string) => {
26
+ if (/^(https?:|mailto:|tel:)/.test(path)) {
27
+ window.open(path, '_blank', 'noopener,noreferrer');
28
+ return;
29
+ }
30
+ onNavigate(path);
31
+ window.scrollTo({ top: 0, behavior: 'smooth' });
32
+ };
33
+
34
+ const footerMenu = siteConfig?.menus.find((m) => m.id === 'footer')?.items;
35
+ const col1 = siteConfig?.menus.find((m) => m.id === 'footer-col-1')?.items;
36
+ const col2 = siteConfig?.menus.find((m) => m.id === 'footer-col-2')?.items;
37
+ const col3 = siteConfig?.menus.find((m) => m.id === 'footer-col-3')?.items;
38
+
39
+ const legalLinks =
40
+ footerMenu?.filter((i) => LEGAL.includes(i.path)).map((i) => ({ label: i.label, path: i.path })) ||
41
+ [
42
+ { label: 'Mentions légales', path: '/mentions-legales/' },
43
+ { label: 'Politique de confidentialité', path: '/politique-de-confidentialite/' },
44
+ { label: 'Cookies', path: '/cookies/' },
45
+ ];
46
+
47
+ const companyLinks = footerMenu?.filter((i) => !LEGAL.includes(i.path)).map((i) => ({ label: i.label, path: i.path, highlight: !!i.highlight })) || [];
48
+
49
+ const LinkCol = ({ title, items }: { title: string; items?: { id: string; label: string; path: string; highlight?: boolean }[] }) => {
50
+ if (!items?.length) return null;
51
+ return (
52
+ <div className="space-y-3">
53
+ <div className="text-xs font-semibold uppercase tracking-wider text-slate-300">{title}</div>
54
+ <ul className="space-y-2 text-xs">
55
+ {items.map((l) => (
56
+ <li key={l.id || l.path}>
57
+ <button
58
+ type="button"
59
+ onClick={() => navigateTo(l.path)}
60
+ className={l.highlight ? 'flex items-center gap-1 font-semibold text-px-primary-light hover:underline' : 'text-left text-slate-400 transition-colors hover:text-white'}
61
+ >
62
+ <span>{l.label}</span>
63
+ {l.highlight ? <ArrowUpRight className="h-3.5 w-3.5" /> : null}
64
+ </button>
65
+ </li>
66
+ ))}
67
+ </ul>
68
+ </div>
69
+ );
70
+ };
71
+
72
+ return (
73
+ <footer className="border-t border-slate-800 bg-px-navy pb-24 pt-16 text-white lg:pb-12">
74
+ <div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
75
+ <div className="grid grid-cols-1 gap-10 border-b border-slate-800/80 pb-12 md:grid-cols-2 lg:grid-cols-12 lg:gap-8">
76
+ <div className="space-y-4 lg:col-span-4">
77
+ <div onClick={() => navigateTo('/')} className="group inline-flex cursor-pointer items-center gap-3">
78
+ {siteConfig?.branding.logoDarkUrl ? (
79
+ <img src={siteConfig.branding.logoDarkUrl} alt={SITE_INFO.name} className="h-11 w-auto max-w-[200px] object-contain" />
80
+ ) : (
81
+ <>
82
+ <SnapifyLogo className="h-9 w-9 !border-slate-700 !bg-slate-900" />
83
+ <div>
84
+ <div className="flex items-center gap-1">
85
+ <span className="text-xl font-bold tracking-tight text-white">{SITE_INFO.name.replace(/®$/, '')}</span>
86
+ {SITE_INFO.name.endsWith('®') && <span className="text-[10px] font-bold text-slate-400">®</span>}
87
+ </div>
88
+ <div className="text-[11px] font-normal leading-none text-slate-400">{SITE_INFO.baseline}</div>
89
+ </div>
90
+ </>
91
+ )}
92
+ </div>
93
+ <div className="text-xs font-medium tracking-wide text-px-primary-light">{SITE_INFO.slogan}</div>
94
+ <p className="max-w-sm text-xs leading-relaxed text-slate-400">{SITE_INFO.baseline}</p>
95
+ <div className="pt-2">
96
+ <a
97
+ href={`tel:${SITE_INFO.phoneClean}`}
98
+ className="inline-flex items-center gap-2 rounded-lg border border-slate-700/60 bg-slate-900/60 px-3 py-2 text-sm font-semibold text-white transition-colors hover:text-px-primary-light"
99
+ >
100
+ <Phone className="h-4 w-4 text-px-primary" />
101
+ <span className="font-mono">{SITE_INFO.phone}</span>
102
+ </a>
103
+ </div>
104
+ </div>
105
+ <div className="lg:col-span-3">
106
+ <LinkCol title={col1?.[0] ? 'Navigation' : 'Liens'} items={col1?.length ? col1 : companyLinks.slice(0, Math.ceil(companyLinks.length / 2)).map((l, i) => ({ id: `c${i}`, ...l }))} />
107
+ </div>
108
+ <div className="lg:col-span-2">
109
+ <LinkCol title="Explorer" items={col2?.length ? col2 : companyLinks.slice(Math.ceil(companyLinks.length / 2)).map((l, i) => ({ id: `d${i}`, ...l }))} />
110
+ </div>
111
+ <div className="lg:col-span-3">
112
+ <LinkCol title="Contact" items={col3?.length ? col3 : companyLinks.filter((l) => l.highlight).map((l, i) => ({ id: `e${i}`, ...l }))} />
113
+ <div className="mt-4 border-t border-slate-800 pt-4 text-[11px] text-slate-400">
114
+ <div className="mb-1 font-semibold text-slate-300">{SITE_INFO.legalName || SITE_INFO.name}</div>
115
+ <div>{SITE_INFO.address}</div>
116
+ </div>
117
+ </div>
118
+ </div>
119
+
120
+ {siteConfig?.newsletterEnabled ? (
121
+ <div className="border-b border-slate-800/80 py-8">
122
+ <div className="max-w-md">
123
+ <p className="mb-2 text-xs font-semibold uppercase tracking-wider text-slate-300">Newsletter</p>
124
+ <form
125
+ className="flex gap-2"
126
+ onSubmit={async (e) => {
127
+ e.preventDefault();
128
+ setNewsMsg('');
129
+ const r = await fetch('/api/public/newsletter/subscribe', {
130
+ method: 'POST',
131
+ headers: { 'Content-Type': 'application/json' },
132
+ body: JSON.stringify({ email: newsEmail }),
133
+ });
134
+ const j = await r.json().catch(() => ({}));
135
+ if (r.ok) {
136
+ setNewsMsg(j.pending ? 'Vérifiez votre boîte mail pour confirmer.' : 'Inscription enregistrée.');
137
+ setNewsEmail('');
138
+ } else setNewsMsg(j.error || 'Erreur');
139
+ }}
140
+ >
141
+ <input
142
+ type="email"
143
+ required
144
+ value={newsEmail}
145
+ onChange={(e) => setNewsEmail(e.target.value)}
146
+ placeholder="E-mail professionnel"
147
+ className="flex-1 rounded-lg border border-slate-700 bg-slate-900 px-3 py-2 text-xs text-white"
148
+ />
149
+ <button type="submit" className="rounded-lg bg-px-primary px-4 py-2 text-xs font-semibold text-px-navy">
150
+ S&apos;inscrire
151
+ </button>
152
+ </form>
153
+ {newsMsg ? <p className="mt-2 text-[11px] text-px-primary-light">{newsMsg}</p> : null}
154
+ </div>
155
+ </div>
156
+ ) : null}
157
+
158
+ <div className="flex flex-col items-center justify-between gap-6 pt-8 text-xs text-slate-400 lg:flex-row">
159
+ <div className="flex flex-wrap items-center justify-center gap-2 sm:gap-4 lg:justify-start">
160
+ <div className="inline-flex items-center gap-1.5 rounded border border-slate-800 bg-slate-900 px-2.5 py-1 text-[11px] text-slate-300">
161
+ <ShieldCheck className="h-3.5 w-3.5 text-px-primary" />
162
+ <span>{SITE_INFO.slaHours || 'Support réactif'}</span>
163
+ </div>
164
+ <div className="inline-flex items-center gap-1.5 rounded border border-slate-800 bg-slate-900 px-2.5 py-1 text-[11px] text-slate-300">
165
+ <CheckCircle className="h-3.5 w-3.5 text-px-primary" />
166
+ <span>{SITE_INFO.address ? 'Présence locale' : 'France'}</span>
167
+ </div>
168
+ </div>
169
+ <div className="flex flex-wrap items-center justify-center gap-x-4 gap-y-2 text-[11px]">
170
+ <span>
171
+ © {new Date().getFullYear()} {SITE_INFO.name}
172
+ </span>
173
+ {legalLinks.map((l) => (
174
+ <React.Fragment key={l.path}>
175
+ <span aria-hidden="true">·</span>
176
+ <button type="button" onClick={() => navigateTo(l.path)} className="underline-offset-2 transition-colors hover:text-white hover:underline">
177
+ {l.label}
178
+ </button>
179
+ </React.Fragment>
180
+ ))}
181
+ <span aria-hidden="true">·</span>
182
+ <button type="button" onClick={onOpenCookies} className="font-medium underline underline-offset-2 transition-colors hover:text-px-primary-light">
183
+ Gérer mes cookies
184
+ </button>
185
+ </div>
186
+ </div>
187
+ </div>
188
+ </footer>
189
+ );
190
+ };