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.
- package/LICENSE.md +24 -0
- package/README.md +55 -2
- package/bin/create-snapify-app.mjs +174 -0
- package/lib/args.mjs +59 -0
- package/lib/copy.mjs +44 -0
- package/lib/env.mjs +84 -0
- package/lib/prompt.mjs +63 -0
- package/package.json +33 -4
- package/template/.env.example +79 -0
- package/template/.github/workflows/ci.yml +23 -0
- package/template/.github/workflows/deploy.yml +26 -0
- package/template/.github/workflows/ping.yml +8 -0
- package/template/.github/workflows/release.yml +67 -0
- package/template/.snapify-install.json +5 -0
- package/template/CHANGELOG.md +28 -0
- package/template/LICENSE +14 -0
- package/template/README.md +287 -0
- package/template/administrator.html +23 -0
- package/template/apps/client/src/App.tsx +765 -0
- package/template/apps/client/src/auth.css +400 -0
- package/template/apps/client/src/lib/api.ts +22 -0
- package/template/apps/client/src/main.tsx +20 -0
- package/template/apps/community/src/App.tsx +592 -0
- package/template/apps/community/src/main.tsx +20 -0
- package/template/apps/community/src/styles.css +24 -0
- package/template/apps/developers/src/App.tsx +643 -0
- package/template/apps/developers/src/main.tsx +17 -0
- package/template/apps/developers/src/pages.tsx +1587 -0
- package/template/apps/developers/src/styles.css +98 -0
- package/template/apps/developers/src/ui.tsx +179 -0
- package/template/apps/landing/src/App.tsx +598 -0
- package/template/apps/landing/src/LegalPages.tsx +158 -0
- package/template/apps/landing/src/OrderPage.tsx +381 -0
- package/template/apps/landing/src/SeoPages.tsx +923 -0
- package/template/apps/landing/src/copy.ts +195 -0
- package/template/apps/landing/src/main.tsx +20 -0
- package/template/apps/landing/src/styles.css +803 -0
- package/template/apps/landing/src/types.ts +19 -0
- package/template/apps/marketplace/src/App.tsx +1229 -0
- package/template/apps/marketplace/src/developer/DeveloperApp.tsx +1266 -0
- package/template/apps/marketplace/src/main.tsx +20 -0
- package/template/apps/marketplace/src/styles.css +1001 -0
- package/template/apps/shared/AuthContext.tsx +65 -0
- package/template/apps/shared/Logo.tsx +33 -0
- package/template/apps/shared/api.ts +47 -0
- package/template/apps/shared/ui.tsx +128 -0
- package/template/apps/superadmin/src/App.tsx +1264 -0
- package/template/apps/superadmin/src/lib/api.ts +20 -0
- package/template/apps/superadmin/src/lib/hooks.ts +20 -0
- package/template/apps/superadmin/src/main.tsx +16 -0
- package/template/apps/superadmin/src/pages/ClientsPage.tsx +185 -0
- package/template/apps/superadmin/src/pages/InvoicesPage.tsx +157 -0
- package/template/apps/superadmin/src/pages/NewsletterPage.tsx +212 -0
- package/template/apps/superadmin/src/pages/OrdersPage.tsx +195 -0
- package/template/apps/superadmin/src/pages/PaymentsPage.tsx +150 -0
- package/template/apps/superadmin/src/pages/PayoutsPage.tsx +133 -0
- package/template/apps/superadmin/src/pages/PublishersPage.tsx +151 -0
- package/template/apps/superadmin/src/pages/SeoControlPage.tsx +692 -0
- package/template/apps/superadmin/src/pages/SettingsPage.tsx +199 -0
- package/template/apps/superadmin/src/pages/SystemPage.tsx +109 -0
- package/template/apps/superadmin/src/pages/TicketsPage.tsx +201 -0
- package/template/apps/superadmin/src/pages/UsersPage.tsx +167 -0
- package/template/client.html +21 -0
- package/template/community/hamdiayari/akhbar/README.md +23 -0
- package/template/community/hamdiayari/akhbar/screenshot.jpg +0 -0
- package/template/community/hamdiayari/akhbar/snapify.theme.json +76 -0
- package/template/community/hamdiayari/akhbar/src/Brand.tsx +26 -0
- package/template/community/hamdiayari/akhbar/src/Footer.tsx +182 -0
- package/template/community/hamdiayari/akhbar/src/Header.tsx +191 -0
- package/template/community/hamdiayari/akhbar/src/Layout.tsx +49 -0
- package/template/community/hamdiayari/akhbar/src/Social.tsx +33 -0
- package/template/community/hamdiayari/akhbar/src/Ticker.tsx +86 -0
- package/template/community/hamdiayari/akhbar/src/Views.tsx +285 -0
- package/template/community/hamdiayari/akhbar/src/blocks/CollectionBlocks.tsx +672 -0
- package/template/community/hamdiayari/akhbar/src/blocks/ContentBlocks.tsx +305 -0
- package/template/community/hamdiayari/akhbar/src/blocks/ConversionBlocks.tsx +569 -0
- package/template/community/hamdiayari/akhbar/src/blocks/HeaderBlocks.tsx +287 -0
- package/template/community/hamdiayari/akhbar/src/index.tsx +51 -0
- package/template/community/hamdiayari/akhbar/src/kit.tsx +357 -0
- package/template/community/hamdiayari/akhbar/src/site.ts +96 -0
- package/template/community/hamdiayari/akhbar/src/styles.css +566 -0
- package/template/community/hamdiayari/akhbar-actualites/README.md +28 -0
- package/template/community/hamdiayari/akhbar-actualites/screenshot.jpg +0 -0
- package/template/community/hamdiayari/akhbar-actualites/snapify.module.json +107 -0
- package/template/community/hamdiayari/akhbar-actualites/src/admin/AudiencePage.tsx +185 -0
- package/template/community/hamdiayari/akhbar-actualites/src/admin/index.tsx +11 -0
- package/template/community/hamdiayari/akhbar-actualites/src/public/Archive.tsx +142 -0
- package/template/community/hamdiayari/akhbar-actualites/src/public/cards.tsx +152 -0
- package/template/community/hamdiayari/akhbar-actualites/src/public/index.tsx +71 -0
- package/template/community/hamdiayari/akhbar-actualites/src/public/posts.ts +103 -0
- package/template/community/hamdiayari/akhbar-actualites/src/public/styles.css +568 -0
- package/template/community/hamdiayari/akhbar-actualites/src/public/widgets.tsx +226 -0
- package/template/community/hamdiayari/akhbar-actualites/src/server.ts +119 -0
- package/template/community/hamdiayari/akhbar-actualites/src/types.ts +25 -0
- package/template/community/hamdiayari/akhbar-urgent/README.md +36 -0
- package/template/community/hamdiayari/akhbar-urgent/screenshot.jpg +0 -0
- package/template/community/hamdiayari/akhbar-urgent/snapify.module.json +64 -0
- package/template/community/hamdiayari/akhbar-urgent/src/admin/FlashesPage.tsx +229 -0
- package/template/community/hamdiayari/akhbar-urgent/src/admin/index.tsx +11 -0
- package/template/community/hamdiayari/akhbar-urgent/src/public/Ticker.tsx +81 -0
- package/template/community/hamdiayari/akhbar-urgent/src/public/index.tsx +63 -0
- package/template/community/hamdiayari/akhbar-urgent/src/public/styles.css +142 -0
- package/template/community/hamdiayari/akhbar-urgent/src/server.ts +101 -0
- package/template/community/hamdiayari/akhbar-urgent/src/types.ts +35 -0
- package/template/community.html +21 -0
- package/template/developers.html +24 -0
- package/template/docker-compose.db.yml +24 -0
- package/template/docs/BACKOFFICE_SNAPIFY.md +130 -0
- package/template/docs/CI_CD.md +66 -0
- package/template/docs/MODULES.md +346 -0
- package/template/docs/PLATFORM_DEPLOY.md +54 -0
- package/template/docs/logo.svg +10 -0
- package/template/drizzle/0000_init.sql +147 -0
- package/template/drizzle/0001_page_project.sql +5 -0
- package/template/drizzle/0002_modules.sql +23 -0
- package/template/drizzle/0003_themes.sql +4 -0
- package/template/drizzle/0004_security.sql +82 -0
- package/template/drizzle/0005_module_records.sql +16 -0
- package/template/drizzle/0006_avatars.sql +4 -0
- package/template/drizzle/0007_platform.sql +264 -0
- package/template/drizzle/0008_mp_validation.sql +3 -0
- package/template/drizzle/0009_saas_ops.sql +77 -0
- package/template/drizzle/0010_cms_chrome.sql +13 -0
- package/template/drizzle/0011_client_portal.sql +26 -0
- package/template/drizzle/meta/0000_snapshot.json +998 -0
- package/template/drizzle/meta/0001_snapshot.json +1004 -0
- package/template/drizzle/meta/_journal.json +90 -0
- package/template/drizzle.config.ts +16 -0
- package/template/ecosystem.snapify.cjs +24 -0
- package/template/for.html +1 -0
- package/template/index.html +29 -0
- package/template/landing.html +37 -0
- package/template/marketplace/catalog.json +373 -0
- package/template/marketplace/gallery/hamdiayari.preveris/01-home.jpg +0 -0
- package/template/marketplace/gallery/hamdiayari.recaptcha/01-reglages.jpg +0 -0
- package/template/marketplace/gallery/hamdiayari.recaptcha/02-logo.jpg +0 -0
- package/template/marketplace/gallery/novalabs.ai-articles/01-generer.jpg +0 -0
- package/template/marketplace/gallery/novalabs.ai-articles/02-fournisseur-ia.jpg +0 -0
- package/template/marketplace/gallery/novalabs.calendar/01-agenda-site.jpg +0 -0
- package/template/marketplace/gallery/novalabs.calendar/02-evenements-admin.jpg +0 -0
- package/template/marketplace/gallery/novalabs.calendar/03-reglages.jpg +0 -0
- package/template/marketplace/screenshots/hamdiayari.preveris-1.0.6.jpg +0 -0
- package/template/marketplace/screenshots/hamdiayari.recaptcha-1.0.0.jpg +0 -0
- package/template/marketplace/screenshots/hamdiayari.wp-seo-migrator-1.1.1.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.ai-articles-1.0.1.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.ai-chat-1.0.1.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.calendar-1.0.1.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.cart-1.0.0.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.checkout-1.0.0.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.customers-1.1.0.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.dogga-1.0.0.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.mahdia-1.0.0.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.shop-1.0.0.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.sidibou-1.0.0.jpg +0 -0
- package/template/marketplace/screenshots/novalabs.whatsapp-1.0.1.jpg +0 -0
- package/template/marketplace.html +21 -0
- package/template/metadata.json +7 -0
- package/template/modules/ai-articles/screenshot.jpg +0 -0
- package/template/modules/ai-articles/snapify.module.json +74 -0
- package/template/modules/ai-articles/src/admin/ArticlesPage.tsx +162 -0
- package/template/modules/ai-articles/src/admin/GeneratePage.tsx +118 -0
- package/template/modules/ai-articles/src/admin/index.tsx +12 -0
- package/template/modules/ai-articles/src/public/index.tsx +119 -0
- package/template/modules/ai-articles/src/server.ts +287 -0
- package/template/modules/ai-articles/src/types.ts +30 -0
- package/template/modules/ai-chat/screenshot.jpg +0 -0
- package/template/modules/ai-chat/snapify.module.json +217 -0
- package/template/modules/ai-chat/src/public.tsx +198 -0
- package/template/modules/ai-chat/src/server.ts +142 -0
- package/template/modules/ai-theme-studio/README.md +15 -0
- package/template/modules/ai-theme-studio/screenshot.jpg +0 -0
- package/template/modules/ai-theme-studio/snapify.module.json +175 -0
- package/template/modules/ai-theme-studio/src/admin/HistoryPage.tsx +91 -0
- package/template/modules/ai-theme-studio/src/admin/StudioPage.tsx +414 -0
- package/template/modules/ai-theme-studio/src/admin/index.tsx +11 -0
- package/template/modules/ai-theme-studio/src/lib/images.ts +240 -0
- package/template/modules/ai-theme-studio/src/lib/llm.ts +123 -0
- package/template/modules/ai-theme-studio/src/lib/normalize.ts +714 -0
- package/template/modules/ai-theme-studio/src/lib/prompts.ts +198 -0
- package/template/modules/ai-theme-studio/src/server.ts +137 -0
- package/template/modules/ai-theme-studio/src/types.ts +112 -0
- package/template/modules/calendar/screenshot.jpg +0 -0
- package/template/modules/calendar/snapify.module.json +45 -0
- package/template/modules/calendar/src/admin/EventsPage.tsx +217 -0
- package/template/modules/calendar/src/admin/index.tsx +11 -0
- package/template/modules/calendar/src/public/index.tsx +231 -0
- package/template/modules/calendar/src/server.ts +143 -0
- package/template/modules/calendar/src/types.ts +25 -0
- package/template/modules/cart/screenshot.jpg +0 -0
- package/template/modules/cart/snapify.module.json +90 -0
- package/template/modules/cart/src/admin/index.tsx +265 -0
- package/template/modules/cart/src/public/CartPage.tsx +198 -0
- package/template/modules/cart/src/public/index.tsx +261 -0
- package/template/modules/cart/src/public/useQuote.ts +67 -0
- package/template/modules/cart/src/server.ts +148 -0
- package/template/modules/cart/src/types.ts +31 -0
- package/template/modules/checkout/screenshot.jpg +0 -0
- package/template/modules/checkout/snapify.module.json +264 -0
- package/template/modules/checkout/src/admin/OrderDetailPage.tsx +304 -0
- package/template/modules/checkout/src/admin/badges.tsx +28 -0
- package/template/modules/checkout/src/admin/index.tsx +236 -0
- package/template/modules/checkout/src/public/CheckoutPage.tsx +461 -0
- package/template/modules/checkout/src/public/OrderPages.tsx +153 -0
- package/template/modules/checkout/src/public/index.tsx +23 -0
- package/template/modules/checkout/src/server.ts +383 -0
- package/template/modules/checkout/src/types.ts +70 -0
- package/template/modules/customers/screenshot.jpg +0 -0
- package/template/modules/customers/snapify.module.json +115 -0
- package/template/modules/customers/src/admin/CustomerDetailPage.tsx +274 -0
- package/template/modules/customers/src/admin/index.tsx +194 -0
- package/template/modules/customers/src/public/AccountPages.tsx +470 -0
- package/template/modules/customers/src/public/AddressForm.tsx +115 -0
- package/template/modules/customers/src/public/AuthPages.tsx +349 -0
- package/template/modules/customers/src/public/Avatar.tsx +29 -0
- package/template/modules/customers/src/public/ProfileSection.tsx +291 -0
- package/template/modules/customers/src/public/index.tsx +137 -0
- package/template/modules/customers/src/public/session.ts +51 -0
- package/template/modules/customers/src/server.ts +421 -0
- package/template/modules/customers/src/types.ts +53 -0
- package/template/modules/recaptcha/logo.jpg +0 -0
- package/template/modules/recaptcha/screenshot.jpg +0 -0
- package/template/modules/recaptcha/snapify.module.json +99 -0
- package/template/modules/recaptcha/src/public/index.tsx +94 -0
- package/template/modules/recaptcha/src/server.ts +65 -0
- package/template/modules/recaptcha/src/types.ts +18 -0
- package/template/modules/shop/screenshot.jpg +0 -0
- package/template/modules/shop/snapify.module.json +340 -0
- package/template/modules/shop/src/admin/CategoriesPage.tsx +224 -0
- package/template/modules/shop/src/admin/ProductEditor.tsx +313 -0
- package/template/modules/shop/src/admin/ProductsPage.tsx +307 -0
- package/template/modules/shop/src/admin/index.tsx +14 -0
- package/template/modules/shop/src/public/Catalog.tsx +244 -0
- package/template/modules/shop/src/public/NotFound.tsx +22 -0
- package/template/modules/shop/src/public/ProductPage.tsx +195 -0
- package/template/modules/shop/src/public/index.tsx +134 -0
- package/template/modules/shop/src/public/useFetch.ts +26 -0
- package/template/modules/shop/src/server.ts +493 -0
- package/template/modules/shop/src/shared.ts +41 -0
- package/template/modules/shop/src/types.ts +60 -0
- package/template/modules/whatsapp/screenshot.jpg +0 -0
- package/template/modules/whatsapp/snapify.module.json +121 -0
- package/template/modules/whatsapp/src/public.tsx +119 -0
- package/template/modules/wp-seo-migrator/ARCHITECTURE.md +113 -0
- package/template/modules/wp-seo-migrator/README.md +21 -0
- package/template/modules/wp-seo-migrator/fixtures/aioseo-sample.csv +3 -0
- package/template/modules/wp-seo-migrator/fixtures/sample-exporter.json +39 -0
- package/template/modules/wp-seo-migrator/fixtures/sample-page.html +12 -0
- package/template/modules/wp-seo-migrator/fixtures/sample.htaccess +3 -0
- package/template/modules/wp-seo-migrator/screenshot.jpg +0 -0
- package/template/modules/wp-seo-migrator/snapify.module.json +87 -0
- package/template/modules/wp-seo-migrator/src/admin/HistoryPage.tsx +64 -0
- package/template/modules/wp-seo-migrator/src/admin/WizardPage.tsx +788 -0
- package/template/modules/wp-seo-migrator/src/admin/i18n.ts +156 -0
- package/template/modules/wp-seo-migrator/src/admin/index.tsx +11 -0
- package/template/modules/wp-seo-migrator/src/connectors/CUSTOM_CONNECTOR.md +31 -0
- package/template/modules/wp-seo-migrator/src/connectors/csvSeoFactory.ts +106 -0
- package/template/modules/wp-seo-migrator/src/connectors/exporter.ts +96 -0
- package/template/modules/wp-seo-migrator/src/connectors/gscCsv.ts +53 -0
- package/template/modules/wp-seo-migrator/src/connectors/htaccess.ts +65 -0
- package/template/modules/wp-seo-migrator/src/connectors/htmlCrawler.ts +90 -0
- package/template/modules/wp-seo-migrator/src/connectors/index.ts +113 -0
- package/template/modules/wp-seo-migrator/src/connectors/pluginCsv.ts +77 -0
- package/template/modules/wp-seo-migrator/src/connectors/rankMathCsv.ts +77 -0
- package/template/modules/wp-seo-migrator/src/connectors/redirection.ts +62 -0
- package/template/modules/wp-seo-migrator/src/connectors/sitemap.ts +44 -0
- package/template/modules/wp-seo-migrator/src/connectors/utils.ts +111 -0
- package/template/modules/wp-seo-migrator/src/connectors/wxr.ts +103 -0
- package/template/modules/wp-seo-migrator/src/connectors/yoastCsv.ts +56 -0
- package/template/modules/wp-seo-migrator/src/pipeline/analyze.ts +158 -0
- package/template/modules/wp-seo-migrator/src/pipeline/htmlParse.ts +58 -0
- package/template/modules/wp-seo-migrator/src/pipeline/remoteAuto.ts +328 -0
- package/template/modules/wp-seo-migrator/src/server.ts +492 -0
- package/template/modules/wp-seo-migrator/src/types.ts +151 -0
- package/template/modules/wp-seo-migrator/tests/connectors-extra.test.ts +39 -0
- package/template/modules/wp-seo-migrator/tests/merge.test.ts +25 -0
- package/template/package-lock.json +6016 -0
- package/template/package.json +74 -0
- package/template/public/images/photos/caravane-chameaux-desert.webp +0 -0
- package/template/public/images/photos/hammamet-medina-plage.jpg +0 -0
- package/template/public/images/photos/maison-campagne-collines.webp +0 -0
- package/template/public/images/photos/maisons-troglodytes-desert.jpg +0 -0
- package/template/public/images/photos/panorama-ville.jpg +0 -0
- package/template/public/images/photos/preveris-accessibilite-erp.jpg +0 -0
- package/template/public/images/photos/preveris-collectivites.jpg +0 -0
- package/template/public/images/photos/preveris-etudes-plans.jpg +0 -0
- package/template/public/images/photos/preveris-hero-erp.jpg +0 -0
- package/template/public/images/photos/preveris-ssi-incendie.jpg +0 -0
- package/template/public/images/photos/preveris-suivi-hotel.jpg +0 -0
- package/template/public/images/photos/ruelle-bleue-medina.jpg +0 -0
- package/template/public/images/photos/sidi-bou-said-port.webp +0 -0
- package/template/public/images/photos/terrasse-blanche-mer-voilier.jpg +0 -0
- package/template/public/landing/hero-ai-cms.jpg +0 -0
- package/template/public/landing/shot-dogga.jpg +0 -0
- package/template/public/landing/shot-mahdia.jpg +0 -0
- package/template/public/landing/shot-sidibou.jpg +0 -0
- package/template/public/llms.txt +35 -0
- package/template/public/logo.svg +10 -0
- package/template/scripts/app-pack.ts +155 -0
- package/template/scripts/app-publish.ts +100 -0
- package/template/scripts/apply-ecommerce-home.ts +196 -0
- package/template/scripts/auto-deploy-poll.sh +13 -0
- package/template/scripts/db-backup.sh +16 -0
- package/template/scripts/db-restore.sh +11 -0
- package/template/scripts/deploy.sh +34 -0
- package/template/scripts/github-webhook-deploy.sh +7 -0
- package/template/scripts/install-new-modules.ts +96 -0
- package/template/scripts/refresh-demo-baseline.ts +9 -0
- package/template/scripts/reinstall-marketplace-packs.ts +51 -0
- package/template/scripts/restore-classic-home.ts +13 -0
- package/template/scripts/sdk-pack.ts +289 -0
- package/template/scripts/sdk-publish.ts +75 -0
- package/template/scripts/seed-demo-products.ts +120 -0
- package/template/scripts/seed-theme-content-packs.ts +42 -0
- package/template/scripts/snapify-module.ts +207 -0
- package/template/scripts/test-theme-switch.ts +24 -0
- package/template/sdk/admin.css +18 -0
- package/template/sdk/admin.ts +95 -0
- package/template/sdk/build.ts +249 -0
- package/template/sdk/commerce.ts +512 -0
- package/template/sdk/content.ts +76 -0
- package/template/sdk/icons.ts +101 -0
- package/template/sdk/image.ts +60 -0
- package/template/sdk/index.ts +500 -0
- package/template/sdk/package.ts +298 -0
- package/template/sdk/recaptcha.tsx +122 -0
- package/template/sdk/settings.ts +84 -0
- package/template/sdk/theme.css +29 -0
- package/template/sdk/theme.ts +100 -0
- package/template/sdk/ui/commerce.tsx +385 -0
- package/template/sdk/ui/orders.tsx +136 -0
- package/template/sdk-kit/CHANGELOG.md +17 -0
- package/template/sdk-kit/LICENSE.md +47 -0
- package/template/sdk-kit/README.md +81 -0
- package/template/sdk-kit/cli.ts +339 -0
- package/template/sdk-kit/create-snapify/README.md +28 -0
- package/template/sdk-kit/create-snapify/create-snapify.mjs +11 -0
- package/template/sdk-kit/create-snapify/package.template.json +16 -0
- package/template/sdk-kit/package.template.json +95 -0
- package/template/sdk-kit/templates/common/README.md.tpl +27 -0
- package/template/sdk-kit/templates/common/_gitignore +3 -0
- package/template/sdk-kit/templates/common/package.json.tpl +19 -0
- package/template/sdk-kit/templates/common/src/snapify-env.d.ts +2 -0
- package/template/sdk-kit/templates/common/tsconfig.json +15 -0
- package/template/sdk-kit/templates/module/snapify.module.json.tpl +33 -0
- package/template/sdk-kit/templates/module/src/admin.tsx +53 -0
- package/template/sdk-kit/templates/module/src/public.tsx +49 -0
- package/template/sdk-kit/templates/module/src/server.ts +30 -0
- package/template/sdk-kit/templates/module/src/types.ts +19 -0
- package/template/sdk-kit/templates/theme/snapify.theme.json.tpl +22 -0
- package/template/sdk-kit/templates/theme/src/Footer.tsx +40 -0
- package/template/sdk-kit/templates/theme/src/Header.tsx +47 -0
- package/template/sdk-kit/templates/theme/src/Views.tsx +36 -0
- package/template/sdk-kit/templates/theme/src/blocks.tsx +93 -0
- package/template/sdk-kit/templates/theme/src/index.tsx +30 -0
- package/template/sdk-kit/templates/theme/src/site.tsx +59 -0
- package/template/sdk-kit/templates/theme/src/styles.css +71 -0
- package/template/server/client/routes.ts +252 -0
- package/template/server/cms/analyticsStore.ts +180 -0
- package/template/server/cms/auth.ts +155 -0
- package/template/server/cms/blockCatalog.ts +342 -0
- package/template/server/cms/blocks.ts +521 -0
- package/template/server/cms/contentMigration.ts +1119 -0
- package/template/server/cms/defaults.ts +303 -0
- package/template/server/cms/permissions.ts +154 -0
- package/template/server/cms/restoreClassicHome.ts +183 -0
- package/template/server/cms/revisions.ts +99 -0
- package/template/server/cms/scheduler.ts +78 -0
- package/template/server/cms/seed.ts +72 -0
- package/template/server/cms/seoAnalyzer.ts +213 -0
- package/template/server/cms/store.ts +122 -0
- package/template/server/cms/themeContent.ts +1386 -0
- package/template/server/cms/themePacks/sidibou-branding.json +1 -0
- package/template/server/cms/themePacks/sidibou-home.json +1 -0
- package/template/server/cms/themePacks/sidibou-menus.json +1 -0
- package/template/server/cms/types.ts +432 -0
- package/template/server/community/routes.ts +410 -0
- package/template/server/db/client.ts +43 -0
- package/template/server/db/repository.ts +389 -0
- package/template/server/db/schema.ts +726 -0
- package/template/server/demo/cmsReset.ts +196 -0
- package/template/server/developers/sdk.ts +85 -0
- package/template/server/geoip.ts +15 -0
- package/template/server/http.ts +53 -0
- package/template/server/mail.ts +216 -0
- package/template/server/marketplace/catalog.ts +74 -0
- package/template/server/marketplace/packageValidate.ts +475 -0
- package/template/server/marketplace/payments.ts +193 -0
- package/template/server/marketplace/routes.ts +850 -0
- package/template/server/marketplace/signing.ts +116 -0
- package/template/server/marketplace/syncOfficial.ts +282 -0
- package/template/server/media/images.ts +63 -0
- package/template/server/middleware/adminAuth.ts +134 -0
- package/template/server/modules/legacy.ts +44 -0
- package/template/server/modules/manager.ts +1122 -0
- package/template/server/modules/marketplace.ts +178 -0
- package/template/server/modules/outbound.ts +178 -0
- package/template/server/modules/purchases.ts +149 -0
- package/template/server/modules/records.ts +168 -0
- package/template/server/modules/themeHtml.ts +32 -0
- package/template/server/modules/trust.ts +16 -0
- package/template/server/modules/types.ts +147 -0
- package/template/server/platform/auth.ts +223 -0
- package/template/server/platform/clientWelcome.ts +168 -0
- package/template/server/platform/clients.ts +139 -0
- package/template/server/platform/dailyImprove.ts +356 -0
- package/template/server/platform/flouci.ts +126 -0
- package/template/server/platform/host.ts +69 -0
- package/template/server/platform/hostingProviders.ts +143 -0
- package/template/server/platform/licenseOrders.ts +291 -0
- package/template/server/platform/money.ts +42 -0
- package/template/server/platform/passwords.ts +41 -0
- package/template/server/platform/region.ts +86 -0
- package/template/server/platform/routes.ts +225 -0
- package/template/server/platform/seed.ts +82 -0
- package/template/server/platform/settings.ts +72 -0
- package/template/server/routes/adminAccount.ts +403 -0
- package/template/server/routes/adminCms.ts +808 -0
- package/template/server/routes/adminSystem.ts +243 -0
- package/template/server/routes/adminUsers.ts +449 -0
- package/template/server/routes/googleOAuth.ts +327 -0
- package/template/server/routes/modules.ts +323 -0
- package/template/server/routes/publicCms.ts +313 -0
- package/template/server/routes/seoPublic.ts +158 -0
- package/template/server/security/accounts.ts +121 -0
- package/template/server/security/audit.ts +143 -0
- package/template/server/security/authTokens.ts +74 -0
- package/template/server/security/ip.ts +68 -0
- package/template/server/security/rateLimit.ts +85 -0
- package/template/server/security/retention.ts +46 -0
- package/template/server/security/secrets.ts +44 -0
- package/template/server/security/sessions.ts +140 -0
- package/template/server/security/totp.ts +101 -0
- package/template/server/superadmin/analytics.ts +109 -0
- package/template/server/superadmin/auth.ts +92 -0
- package/template/server/superadmin/backlinksCatalog.ts +210 -0
- package/template/server/superadmin/frSeoContent.ts +165 -0
- package/template/server/superadmin/landingSeo.ts +675 -0
- package/template/server/superadmin/ops.ts +238 -0
- package/template/server/superadmin/routes.ts +1095 -0
- package/template/server/superadmin/tnSeoContent.ts +130 -0
- package/template/server.ts +522 -0
- package/template/src/App.tsx +263 -0
- package/template/src/admin/AdminApp.tsx +162 -0
- package/template/src/admin/account/AuthLayout.tsx +57 -0
- package/template/src/admin/account/SecurityForms.tsx +276 -0
- package/template/src/admin/admin.css +127 -0
- package/template/src/admin/context/AuthContext.tsx +147 -0
- package/template/src/admin/context/SiteContext.tsx +50 -0
- package/template/src/admin/editor/BlockLibrary.tsx +173 -0
- package/template/src/admin/editor/BlocksPanel.tsx +396 -0
- package/template/src/admin/editor/EditorBits.tsx +106 -0
- package/template/src/admin/editor/PreviewFrame.tsx +145 -0
- package/template/src/admin/editor/RichTextEditor.tsx +276 -0
- package/template/src/admin/editor/SeoPanel.tsx +349 -0
- package/template/src/admin/editor/StyleEditor.tsx +114 -0
- package/template/src/admin/editor/blockSchemas.ts +699 -0
- package/template/src/admin/editor/fields.tsx +486 -0
- package/template/src/admin/layout/AdminShell.tsx +418 -0
- package/template/src/admin/layout/nav.ts +95 -0
- package/template/src/admin/lib/api.ts +408 -0
- package/template/src/admin/lib/audit.ts +141 -0
- package/template/src/admin/lib/format.ts +84 -0
- package/template/src/admin/lib/hooks.ts +125 -0
- package/template/src/admin/main.tsx +16 -0
- package/template/src/admin/modules/InstallDialog.tsx +160 -0
- package/template/src/admin/modules/SettingsForm.tsx +145 -0
- package/template/src/admin/modules/kit.tsx +127 -0
- package/template/src/admin/modules/labels.tsx +226 -0
- package/template/src/admin/pages/AccountSetupPage.tsx +62 -0
- package/template/src/admin/pages/AnalyticsPage.tsx +206 -0
- package/template/src/admin/pages/BrandingPage.tsx +154 -0
- package/template/src/admin/pages/DashboardPage.tsx +239 -0
- package/template/src/admin/pages/EngagementPage.tsx +168 -0
- package/template/src/admin/pages/FormsPage.tsx +435 -0
- package/template/src/admin/pages/GeneralPage.tsx +120 -0
- package/template/src/admin/pages/LoginPage.tsx +249 -0
- package/template/src/admin/pages/MaintenancePage.tsx +125 -0
- package/template/src/admin/pages/MarketplacePage.tsx +426 -0
- package/template/src/admin/pages/MediaPage.tsx +251 -0
- package/template/src/admin/pages/MenusPage.tsx +446 -0
- package/template/src/admin/pages/ModuleAdminPage.tsx +113 -0
- package/template/src/admin/pages/ModuleDetailPage.tsx +293 -0
- package/template/src/admin/pages/ModulesPage.tsx +135 -0
- package/template/src/admin/pages/NewsletterPage.tsx +193 -0
- package/template/src/admin/pages/PageEditorPage.tsx +322 -0
- package/template/src/admin/pages/PagesListPage.tsx +277 -0
- package/template/src/admin/pages/PostEditorPage.tsx +292 -0
- package/template/src/admin/pages/PostsListPage.tsx +187 -0
- package/template/src/admin/pages/ProfilePage.tsx +489 -0
- package/template/src/admin/pages/ResetPasswordPage.tsx +110 -0
- package/template/src/admin/pages/SeoPage.tsx +670 -0
- package/template/src/admin/pages/SettingsPage.tsx +189 -0
- package/template/src/admin/pages/ThemeCustomizePage.tsx +170 -0
- package/template/src/admin/pages/ThemesPage.tsx +292 -0
- package/template/src/admin/pages/UsersPage.tsx +57 -0
- package/template/src/admin/pages/WidgetsPage.tsx +200 -0
- package/template/src/admin/seo/GoogleSetup.tsx +529 -0
- package/template/src/admin/settings/EmailTab.tsx +293 -0
- package/template/src/admin/settings/OtherTabs.tsx +270 -0
- package/template/src/admin/settings/SecurityTab.tsx +172 -0
- package/template/src/admin/settings/controls.tsx +133 -0
- package/template/src/admin/ui/Pagination.tsx +170 -0
- package/template/src/admin/ui/SaveBar.tsx +77 -0
- package/template/src/admin/ui/TagInput.tsx +54 -0
- package/template/src/admin/ui/charts.tsx +108 -0
- package/template/src/admin/ui/media.tsx +265 -0
- package/template/src/admin/ui/overlays.tsx +239 -0
- package/template/src/admin/ui/primitives.tsx +544 -0
- package/template/src/admin/users/AuditTab.tsx +104 -0
- package/template/src/admin/users/RolesTab.tsx +368 -0
- package/template/src/admin/users/UsersTab.tsx +723 -0
- package/template/src/admin/users/shared.tsx +278 -0
- package/template/src/cms/icons.ts +7 -0
- package/template/src/components/CookieBanner.tsx +166 -0
- package/template/src/components/MaintenanceGate.tsx +66 -0
- package/template/src/components/SiteAnalytics.tsx +64 -0
- package/template/src/components/SnapifyLogo.tsx +44 -0
- package/template/src/components/cms/BlockRenderer.tsx +154 -0
- package/template/src/components/cms/WidgetArea.tsx +193 -0
- package/template/src/context/SiteConfigContext.tsx +130 -0
- package/template/src/demoBase.ts +23 -0
- package/template/src/index.css +90 -0
- package/template/src/main.tsx +16 -0
- package/template/src/modules/ModuleHost.tsx +197 -0
- package/template/src/preview.ts +63 -0
- package/template/src/runtime.ts +38 -0
- package/template/src/themes/ThemeHost.tsx +168 -0
- package/template/src/themes/fallback.tsx +71 -0
- package/template/src/types.ts +94 -0
- package/template/src/views/BlogPostView.tsx +68 -0
- package/template/src/views/CmsPageView.tsx +34 -0
- package/template/start-pm2.sh +3 -0
- package/template/superadmin.html +20 -0
- package/template/themes/ai-canvas/screenshot.jpg +0 -0
- package/template/themes/ai-canvas/snapify.theme.json +96 -0
- package/template/themes/ai-canvas/src/CeeSimulator.tsx +247 -0
- package/template/themes/ai-canvas/src/Footer.tsx +190 -0
- package/template/themes/ai-canvas/src/Header.tsx +324 -0
- package/template/themes/ai-canvas/src/Layout.tsx +56 -0
- package/template/themes/ai-canvas/src/NotFound.tsx +58 -0
- package/template/themes/ai-canvas/src/Post.tsx +26 -0
- package/template/themes/ai-canvas/src/SnapifyLogo.tsx +44 -0
- package/template/themes/ai-canvas/src/blocks/CollectionBlocks.tsx +711 -0
- package/template/themes/ai-canvas/src/blocks/ContentBlocks.tsx +318 -0
- package/template/themes/ai-canvas/src/blocks/ConversionBlocks.tsx +559 -0
- package/template/themes/ai-canvas/src/blocks/HeaderBlocks.tsx +341 -0
- package/template/themes/ai-canvas/src/index.tsx +54 -0
- package/template/themes/ai-canvas/src/kit.tsx +398 -0
- package/template/themes/ai-canvas/src/site.ts +29 -0
- package/template/themes/ai-canvas/src/styles.css +317 -0
- package/template/themes/dogga/screenshot.jpg +0 -0
- package/template/themes/dogga/snapify.theme.json +55 -0
- package/template/themes/dogga/src/Brand.tsx +41 -0
- package/template/themes/dogga/src/Footer.tsx +202 -0
- package/template/themes/dogga/src/Header.tsx +239 -0
- package/template/themes/dogga/src/Layout.tsx +43 -0
- package/template/themes/dogga/src/Views.tsx +71 -0
- package/template/themes/dogga/src/blocks/CollectionBlocks.tsx +674 -0
- package/template/themes/dogga/src/blocks/ContentBlocks.tsx +307 -0
- package/template/themes/dogga/src/blocks/ConversionBlocks.tsx +571 -0
- package/template/themes/dogga/src/blocks/HeaderBlocks.tsx +289 -0
- package/template/themes/dogga/src/index.tsx +53 -0
- package/template/themes/dogga/src/kit.tsx +362 -0
- package/template/themes/dogga/src/site.ts +56 -0
- package/template/themes/dogga/src/styles.css +396 -0
- package/template/themes/mahdia/screenshot.jpg +0 -0
- package/template/themes/mahdia/snapify.theme.json +65 -0
- package/template/themes/mahdia/src/Brand.tsx +39 -0
- package/template/themes/mahdia/src/Footer.tsx +210 -0
- package/template/themes/mahdia/src/Header.tsx +291 -0
- package/template/themes/mahdia/src/Layout.tsx +44 -0
- package/template/themes/mahdia/src/Views.tsx +79 -0
- package/template/themes/mahdia/src/blocks/CollectionBlocks.tsx +674 -0
- package/template/themes/mahdia/src/blocks/ContentBlocks.tsx +307 -0
- package/template/themes/mahdia/src/blocks/ConversionBlocks.tsx +571 -0
- package/template/themes/mahdia/src/blocks/HeaderBlocks.tsx +289 -0
- package/template/themes/mahdia/src/index.tsx +53 -0
- package/template/themes/mahdia/src/kit.tsx +362 -0
- package/template/themes/mahdia/src/site.ts +75 -0
- package/template/themes/mahdia/src/styles.css +446 -0
- package/template/themes/preveris/screenshot.jpg +0 -0
- package/template/themes/preveris/snapify.theme.json +126 -0
- package/template/themes/preveris/src/Brand.tsx +44 -0
- package/template/themes/preveris/src/Footer.tsx +202 -0
- package/template/themes/preveris/src/Header.tsx +239 -0
- package/template/themes/preveris/src/Layout.tsx +42 -0
- package/template/themes/preveris/src/Views.tsx +71 -0
- package/template/themes/preveris/src/blocks/CollectionBlocks.tsx +678 -0
- package/template/themes/preveris/src/blocks/ContentBlocks.tsx +307 -0
- package/template/themes/preveris/src/blocks/ConversionBlocks.tsx +571 -0
- package/template/themes/preveris/src/blocks/HeaderBlocks.tsx +299 -0
- package/template/themes/preveris/src/index.tsx +53 -0
- package/template/themes/preveris/src/kit.tsx +365 -0
- package/template/themes/preveris/src/site.ts +79 -0
- package/template/themes/preveris/src/styles.css +446 -0
- package/template/themes/sidibou/screenshot.jpg +0 -0
- package/template/themes/sidibou/snapify.theme.json +21 -0
- package/template/themes/sidibou/src/CeeSimulator.tsx +247 -0
- package/template/themes/sidibou/src/Footer.tsx +203 -0
- package/template/themes/sidibou/src/Header.tsx +324 -0
- package/template/themes/sidibou/src/Layout.tsx +53 -0
- package/template/themes/sidibou/src/NotFound.tsx +58 -0
- package/template/themes/sidibou/src/Post.tsx +26 -0
- package/template/themes/sidibou/src/SnapifyLogo.tsx +44 -0
- package/template/themes/sidibou/src/blocks/CollectionBlocks.tsx +711 -0
- package/template/themes/sidibou/src/blocks/ContentBlocks.tsx +318 -0
- package/template/themes/sidibou/src/blocks/ConversionBlocks.tsx +559 -0
- package/template/themes/sidibou/src/blocks/HeaderBlocks.tsx +341 -0
- package/template/themes/sidibou/src/index.tsx +54 -0
- package/template/themes/sidibou/src/kit.tsx +398 -0
- package/template/themes/sidibou/src/site.ts +28 -0
- package/template/themes/sidibou/src/styles.css +317 -0
- package/template/tsconfig.json +33 -0
- package/template/vite.config.ts +42 -0
|
@@ -0,0 +1,643 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapify Developers — portail de documentation (developers.snapify.tn)
|
|
3
|
+
* © 2026 NOVALABS WEB DESIGN — Tunisie
|
|
4
|
+
*/
|
|
5
|
+
import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react';
|
|
6
|
+
import { Link, Navigate, NavLink, Route, Routes, useLocation, useNavigate, useParams } from 'react-router-dom';
|
|
7
|
+
import {
|
|
8
|
+
ArrowRight,
|
|
9
|
+
ArrowUpRight,
|
|
10
|
+
BookOpen,
|
|
11
|
+
Boxes,
|
|
12
|
+
ChevronLeft,
|
|
13
|
+
ChevronRight,
|
|
14
|
+
Download,
|
|
15
|
+
FileCode2,
|
|
16
|
+
LayoutTemplate,
|
|
17
|
+
Menu,
|
|
18
|
+
PackageCheck,
|
|
19
|
+
Puzzle,
|
|
20
|
+
Search,
|
|
21
|
+
ShieldCheck,
|
|
22
|
+
Store,
|
|
23
|
+
X,
|
|
24
|
+
} from 'lucide-react';
|
|
25
|
+
import { SnapifyMark } from '../../shared/Logo';
|
|
26
|
+
import { API_VERSION, PAGES, SECTIONS, type DocPage } from './pages';
|
|
27
|
+
import { Code, COMMUNITY_URL, CONTACT_EMAIL, MARKETPLACE_URL, SITE_URL } from './ui';
|
|
28
|
+
|
|
29
|
+
const docPath = (p: DocPage) => `/docs/${p.slug}`;
|
|
30
|
+
const norm = (s: string) =>
|
|
31
|
+
s
|
|
32
|
+
.normalize('NFD')
|
|
33
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
34
|
+
.toLowerCase();
|
|
35
|
+
|
|
36
|
+
/* ------------------------------------------------------------------ En-tête */
|
|
37
|
+
|
|
38
|
+
function Brand() {
|
|
39
|
+
return (
|
|
40
|
+
<Link to="/" className="flex shrink-0 items-center gap-2.5">
|
|
41
|
+
<span className="flex h-9 w-9 items-center justify-center rounded-lg bg-navy">
|
|
42
|
+
<SnapifyMark className="h-6 w-6" />
|
|
43
|
+
</span>
|
|
44
|
+
<span className="text-[15px] font-semibold tracking-tight text-navy">Snapify</span>
|
|
45
|
+
<span className="rounded-md bg-teal-50 px-1.5 py-0.5 text-[11px] font-semibold uppercase tracking-wide text-brand-dark ring-1 ring-teal-100">
|
|
46
|
+
Developers
|
|
47
|
+
</span>
|
|
48
|
+
</Link>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const TOP_LINKS = [
|
|
53
|
+
{ to: '/docs/introduction', label: 'Documentation', match: (p: string) => p.startsWith('/docs/') && !p.startsWith('/docs/themes') && !p.startsWith('/docs/publier') && !p.startsWith('/docs/reference') },
|
|
54
|
+
{ to: '/docs/themes/creer-un-theme', label: 'Thèmes', match: (p: string) => p.startsWith('/docs/themes') },
|
|
55
|
+
{ to: '/docs/reference/permissions', label: 'Référence', match: (p: string) => p.startsWith('/docs/reference') },
|
|
56
|
+
{ to: '/docs/publier', label: 'Publier', match: (p: string) => p.startsWith('/docs/publier') },
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
function Header({ onSearch, onMenu, menuOpen }: { onSearch: () => void; onMenu: () => void; menuOpen: boolean }) {
|
|
60
|
+
const { pathname } = useLocation();
|
|
61
|
+
return (
|
|
62
|
+
<header className="sticky top-0 z-40 border-b border-slate-200/80 bg-white/90 backdrop-blur">
|
|
63
|
+
<div className="mx-auto flex h-16 max-w-[1440px] items-center gap-4 px-4 sm:px-6">
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
onClick={onMenu}
|
|
67
|
+
className="-ml-1 inline-flex h-9 w-9 items-center justify-center rounded-lg text-slate-600 hover:bg-slate-100 lg:hidden"
|
|
68
|
+
aria-label={menuOpen ? 'Fermer le menu' : 'Ouvrir le menu'}
|
|
69
|
+
>
|
|
70
|
+
{menuOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
|
|
71
|
+
</button>
|
|
72
|
+
<Brand />
|
|
73
|
+
<nav className="ml-4 hidden items-center gap-1 lg:flex">
|
|
74
|
+
{TOP_LINKS.map((l) => (
|
|
75
|
+
<Link
|
|
76
|
+
key={l.to}
|
|
77
|
+
to={l.to}
|
|
78
|
+
className={`rounded-md px-3 py-2 text-sm font-medium transition ${l.match(pathname) ? 'text-navy' : 'text-slate-500 hover:text-navy'}`}
|
|
79
|
+
>
|
|
80
|
+
{l.label}
|
|
81
|
+
</Link>
|
|
82
|
+
))}
|
|
83
|
+
</nav>
|
|
84
|
+
<div className="ml-auto flex items-center gap-2">
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
onClick={onSearch}
|
|
88
|
+
className="hidden h-9 w-60 items-center gap-2 rounded-lg border border-slate-200 bg-slate-50 px-3 text-sm text-slate-400 transition hover:border-slate-300 md:flex"
|
|
89
|
+
>
|
|
90
|
+
<Search className="h-4 w-4" />
|
|
91
|
+
Rechercher…
|
|
92
|
+
<kbd className="ml-auto rounded border border-slate-200 bg-white px-1.5 font-sans text-[11px] text-slate-400">Ctrl K</kbd>
|
|
93
|
+
</button>
|
|
94
|
+
<button
|
|
95
|
+
type="button"
|
|
96
|
+
onClick={onSearch}
|
|
97
|
+
className="inline-flex h-9 w-9 items-center justify-center rounded-lg text-slate-600 hover:bg-slate-100 md:hidden"
|
|
98
|
+
aria-label="Rechercher"
|
|
99
|
+
>
|
|
100
|
+
<Search className="h-5 w-5" />
|
|
101
|
+
</button>
|
|
102
|
+
<a
|
|
103
|
+
href={MARKETPLACE_URL}
|
|
104
|
+
className="hidden items-center gap-1 rounded-md px-3 py-2 text-sm font-medium text-slate-500 hover:text-navy xl:inline-flex"
|
|
105
|
+
>
|
|
106
|
+
Marketplace <ArrowUpRight className="h-3.5 w-3.5" />
|
|
107
|
+
</a>
|
|
108
|
+
<a
|
|
109
|
+
href={`${MARKETPLACE_URL}/developer`}
|
|
110
|
+
className="hidden h-9 items-center rounded-lg bg-navy px-3.5 text-sm font-semibold text-white transition hover:bg-navy-2 sm:inline-flex"
|
|
111
|
+
>
|
|
112
|
+
Espace vendeur
|
|
113
|
+
</a>
|
|
114
|
+
</div>
|
|
115
|
+
</div>
|
|
116
|
+
</header>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/* ------------------------------------------------------------------ Navigation latérale */
|
|
121
|
+
|
|
122
|
+
function SideNav({ onNavigate }: { onNavigate?: () => void }) {
|
|
123
|
+
return (
|
|
124
|
+
<nav className="space-y-7 text-sm">
|
|
125
|
+
{SECTIONS.map((section) => (
|
|
126
|
+
<div key={section}>
|
|
127
|
+
<div className="mb-2 px-3 text-[11px] font-semibold uppercase tracking-wider text-slate-400">{section}</div>
|
|
128
|
+
<ul className="space-y-0.5">
|
|
129
|
+
{PAGES.filter((p) => p.section === section).map((p) => (
|
|
130
|
+
<li key={p.slug}>
|
|
131
|
+
<NavLink
|
|
132
|
+
to={docPath(p)}
|
|
133
|
+
onClick={onNavigate}
|
|
134
|
+
className={({ isActive }) =>
|
|
135
|
+
`block rounded-md px-3 py-1.5 transition ${isActive ? 'bg-teal-50 font-medium text-brand-dark' : 'text-slate-600 hover:bg-slate-50 hover:text-navy'}`
|
|
136
|
+
}
|
|
137
|
+
>
|
|
138
|
+
{p.title}
|
|
139
|
+
</NavLink>
|
|
140
|
+
</li>
|
|
141
|
+
))}
|
|
142
|
+
</ul>
|
|
143
|
+
</div>
|
|
144
|
+
))}
|
|
145
|
+
<div className="border-t border-slate-100 pt-5">
|
|
146
|
+
<ul className="space-y-0.5">
|
|
147
|
+
{[
|
|
148
|
+
[MARKETPLACE_URL, 'Marketplace'],
|
|
149
|
+
[COMMUNITY_URL, 'Community'],
|
|
150
|
+
[SITE_URL, 'snapify.tn'],
|
|
151
|
+
].map(([href, label]) => (
|
|
152
|
+
<li key={href}>
|
|
153
|
+
<a href={href} className="flex items-center gap-1 rounded-md px-3 py-1.5 text-slate-500 hover:bg-slate-50 hover:text-navy">
|
|
154
|
+
{label} <ArrowUpRight className="h-3.5 w-3.5" />
|
|
155
|
+
</a>
|
|
156
|
+
</li>
|
|
157
|
+
))}
|
|
158
|
+
</ul>
|
|
159
|
+
</div>
|
|
160
|
+
</nav>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function MobileNav({ open, onClose }: { open: boolean; onClose: () => void }) {
|
|
165
|
+
return (
|
|
166
|
+
<div className={`fixed inset-0 top-16 z-30 lg:hidden ${open ? '' : 'pointer-events-none'}`} aria-hidden={!open}>
|
|
167
|
+
<div className={`absolute inset-0 bg-navy/30 transition-opacity ${open ? 'opacity-100' : 'opacity-0'}`} onClick={onClose} />
|
|
168
|
+
<div
|
|
169
|
+
className={`absolute inset-y-0 left-0 w-[86%] max-w-xs overflow-y-auto border-r border-slate-200 bg-white px-3 py-6 shadow-xl transition-transform ${open ? 'translate-x-0' : '-translate-x-full'}`}
|
|
170
|
+
>
|
|
171
|
+
<SideNav onNavigate={onClose} />
|
|
172
|
+
<a
|
|
173
|
+
href={`${MARKETPLACE_URL}/developer`}
|
|
174
|
+
className="mx-3 mt-6 flex h-10 items-center justify-center rounded-lg bg-navy text-sm font-semibold text-white"
|
|
175
|
+
>
|
|
176
|
+
Espace vendeur
|
|
177
|
+
</a>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/* ------------------------------------------------------------------ Recherche */
|
|
184
|
+
|
|
185
|
+
function SearchDialog({ open, onClose }: { open: boolean; onClose: () => void }) {
|
|
186
|
+
const [q, setQ] = useState('');
|
|
187
|
+
const [active, setActive] = useState(0);
|
|
188
|
+
const navigate = useNavigate();
|
|
189
|
+
const input = useRef<HTMLInputElement>(null);
|
|
190
|
+
|
|
191
|
+
const results = useMemo(() => {
|
|
192
|
+
const terms = norm(q).split(/\s+/).filter(Boolean);
|
|
193
|
+
if (!terms.length) return PAGES;
|
|
194
|
+
return PAGES.filter((p) => {
|
|
195
|
+
const hay = norm(`${p.title} ${p.section} ${p.description} ${p.keywords || ''} ${p.slug}`);
|
|
196
|
+
return terms.every((t) => hay.includes(t));
|
|
197
|
+
});
|
|
198
|
+
}, [q]);
|
|
199
|
+
|
|
200
|
+
useEffect(() => {
|
|
201
|
+
if (!open) return;
|
|
202
|
+
setQ('');
|
|
203
|
+
setActive(0);
|
|
204
|
+
window.setTimeout(() => input.current?.focus(), 20);
|
|
205
|
+
}, [open]);
|
|
206
|
+
useEffect(() => setActive(0), [q]);
|
|
207
|
+
|
|
208
|
+
useEffect(() => {
|
|
209
|
+
if (!open) return;
|
|
210
|
+
const onKey = (e: KeyboardEvent) => {
|
|
211
|
+
if (e.key === 'Escape') onClose();
|
|
212
|
+
};
|
|
213
|
+
window.addEventListener('keydown', onKey);
|
|
214
|
+
const prev = document.body.style.overflow;
|
|
215
|
+
document.body.style.overflow = 'hidden';
|
|
216
|
+
return () => {
|
|
217
|
+
window.removeEventListener('keydown', onKey);
|
|
218
|
+
document.body.style.overflow = prev;
|
|
219
|
+
};
|
|
220
|
+
}, [open, onClose]);
|
|
221
|
+
|
|
222
|
+
if (!open) return null;
|
|
223
|
+
const go = (p: DocPage) => {
|
|
224
|
+
navigate(docPath(p));
|
|
225
|
+
onClose();
|
|
226
|
+
};
|
|
227
|
+
return (
|
|
228
|
+
<div
|
|
229
|
+
className="fixed inset-0 z-50 flex items-start justify-center bg-navy/40 px-4 pt-[12vh] backdrop-blur-sm"
|
|
230
|
+
onClick={onClose}
|
|
231
|
+
role="presentation"
|
|
232
|
+
>
|
|
233
|
+
<div
|
|
234
|
+
role="dialog"
|
|
235
|
+
aria-modal="true"
|
|
236
|
+
aria-label="Rechercher dans la documentation"
|
|
237
|
+
className="w-full max-w-xl overflow-hidden rounded-2xl bg-white shadow-2xl ring-1 ring-slate-200"
|
|
238
|
+
onClick={(e) => e.stopPropagation()}
|
|
239
|
+
>
|
|
240
|
+
<div className="flex items-center gap-3 border-b border-slate-100 px-4">
|
|
241
|
+
<Search className="h-5 w-5 text-slate-400" aria-hidden />
|
|
242
|
+
<input
|
|
243
|
+
ref={input}
|
|
244
|
+
value={q}
|
|
245
|
+
onChange={(e) => setQ(e.target.value)}
|
|
246
|
+
aria-autocomplete="list"
|
|
247
|
+
aria-controls="dv-search-results"
|
|
248
|
+
onKeyDown={(e) => {
|
|
249
|
+
if (e.key === 'Escape') onClose();
|
|
250
|
+
if (e.key === 'ArrowDown') {
|
|
251
|
+
e.preventDefault();
|
|
252
|
+
setActive((a) => Math.min(a + 1, results.length - 1));
|
|
253
|
+
}
|
|
254
|
+
if (e.key === 'ArrowUp') {
|
|
255
|
+
e.preventDefault();
|
|
256
|
+
setActive((a) => Math.max(a - 1, 0));
|
|
257
|
+
}
|
|
258
|
+
if (e.key === 'Enter' && results[active]) go(results[active]);
|
|
259
|
+
}}
|
|
260
|
+
placeholder="Manifeste, widgets, permissions, thème…"
|
|
261
|
+
className="h-14 flex-1 bg-transparent text-[15px] text-navy outline-none placeholder:text-slate-400"
|
|
262
|
+
/>
|
|
263
|
+
<kbd className="rounded border border-slate-200 px-1.5 text-[11px] text-slate-400">Échap</kbd>
|
|
264
|
+
</div>
|
|
265
|
+
<ul id="dv-search-results" role="listbox" className="max-h-[55vh] overflow-y-auto p-2">
|
|
266
|
+
{results.map((p, i) => (
|
|
267
|
+
<li key={p.slug}>
|
|
268
|
+
<button
|
|
269
|
+
type="button"
|
|
270
|
+
onMouseEnter={() => setActive(i)}
|
|
271
|
+
onClick={() => go(p)}
|
|
272
|
+
className={`flex w-full items-start gap-3 rounded-lg px-3 py-2.5 text-left ${i === active ? 'bg-teal-50' : ''}`}
|
|
273
|
+
>
|
|
274
|
+
<FileCode2 className={`mt-0.5 h-4 w-4 shrink-0 ${i === active ? 'text-brand-dark' : 'text-slate-400'}`} />
|
|
275
|
+
<span className="min-w-0">
|
|
276
|
+
<span className="block text-sm font-medium text-navy">
|
|
277
|
+
<span className="text-slate-400">{p.section} › </span>
|
|
278
|
+
{p.title}
|
|
279
|
+
</span>
|
|
280
|
+
<span className="block truncate text-[13px] text-slate-500">{p.description}</span>
|
|
281
|
+
</span>
|
|
282
|
+
</button>
|
|
283
|
+
</li>
|
|
284
|
+
))}
|
|
285
|
+
{!results.length && <li className="px-3 py-8 text-center text-sm text-slate-500">Aucun résultat pour « {q} ».</li>}
|
|
286
|
+
</ul>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* ------------------------------------------------------------------ Page de documentation */
|
|
293
|
+
|
|
294
|
+
function Toc({ deps }: { deps: string }) {
|
|
295
|
+
const [items, setItems] = useState<{ id: string; text: string; level: number }[]>([]);
|
|
296
|
+
const [active, setActive] = useState('');
|
|
297
|
+
useEffect(() => {
|
|
298
|
+
const heads = Array.from(document.querySelectorAll<HTMLElement>('article .dv-prose h2, article .dv-prose h3'));
|
|
299
|
+
setItems(heads.map((h) => ({ id: h.id, text: h.textContent || '', level: h.tagName === 'H3' ? 3 : 2 })));
|
|
300
|
+
const obs = new IntersectionObserver(
|
|
301
|
+
(entries) => {
|
|
302
|
+
const visible = entries.filter((e) => e.isIntersecting).sort((a, b) => a.boundingClientRect.top - b.boundingClientRect.top);
|
|
303
|
+
if (visible[0]) setActive(visible[0].target.id);
|
|
304
|
+
},
|
|
305
|
+
{ rootMargin: '-80px 0px -70% 0px' }
|
|
306
|
+
);
|
|
307
|
+
heads.forEach((h) => obs.observe(h));
|
|
308
|
+
return () => obs.disconnect();
|
|
309
|
+
}, [deps]);
|
|
310
|
+
if (items.length < 2) return null;
|
|
311
|
+
return (
|
|
312
|
+
<div className="text-sm">
|
|
313
|
+
<div className="mb-3 text-[11px] font-semibold uppercase tracking-wider text-slate-400">Sur cette page</div>
|
|
314
|
+
<ul className="space-y-1.5 border-l border-slate-200">
|
|
315
|
+
{items.map((it) => (
|
|
316
|
+
<li key={it.id}>
|
|
317
|
+
<a
|
|
318
|
+
href={`#${it.id}`}
|
|
319
|
+
onClick={(e) => {
|
|
320
|
+
e.preventDefault();
|
|
321
|
+
document.getElementById(it.id)?.scrollIntoView({ behavior: 'smooth' });
|
|
322
|
+
history.replaceState(null, '', `#${it.id}`);
|
|
323
|
+
}}
|
|
324
|
+
className={`-ml-px block border-l py-0.5 transition ${it.level === 3 ? 'pl-6' : 'pl-4'} ${active === it.id ? 'border-brand font-medium text-brand-dark' : 'border-transparent text-slate-500 hover:text-navy'}`}
|
|
325
|
+
>
|
|
326
|
+
{it.text}
|
|
327
|
+
</a>
|
|
328
|
+
</li>
|
|
329
|
+
))}
|
|
330
|
+
</ul>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function DocView() {
|
|
336
|
+
const params = useParams();
|
|
337
|
+
const slug = params['*'] || '';
|
|
338
|
+
const index = PAGES.findIndex((p) => p.slug === slug.replace(/\/$/, ''));
|
|
339
|
+
const page = PAGES[index];
|
|
340
|
+
const { hash } = useLocation();
|
|
341
|
+
|
|
342
|
+
useEffect(() => {
|
|
343
|
+
if (!page) return;
|
|
344
|
+
document.title = `${page.title} — Snapify Developers`;
|
|
345
|
+
if (hash) window.setTimeout(() => document.getElementById(hash.slice(1))?.scrollIntoView(), 50);
|
|
346
|
+
else window.scrollTo(0, 0);
|
|
347
|
+
}, [page, hash]);
|
|
348
|
+
|
|
349
|
+
if (!page) return <Navigate to="/docs/introduction" replace />;
|
|
350
|
+
const prev = PAGES[index - 1];
|
|
351
|
+
const next = PAGES[index + 1];
|
|
352
|
+
const Body = page.Body;
|
|
353
|
+
|
|
354
|
+
return (
|
|
355
|
+
<div className="mx-auto flex max-w-[1440px] gap-10 px-4 sm:px-6">
|
|
356
|
+
<aside className="sticky top-16 hidden h-[calc(100vh-4rem)] w-60 shrink-0 overflow-y-auto py-8 pr-2 lg:block">
|
|
357
|
+
<SideNav />
|
|
358
|
+
</aside>
|
|
359
|
+
<article className="min-w-0 flex-1 py-10 lg:py-12">
|
|
360
|
+
<div className="mx-auto max-w-3xl">
|
|
361
|
+
<div className="text-sm font-semibold text-brand-dark">{page.section}</div>
|
|
362
|
+
<h1 className="mt-2 text-3xl font-bold tracking-tight text-navy sm:text-[2.15rem]">{page.title}</h1>
|
|
363
|
+
<p className="mt-3 text-lg leading-relaxed text-slate-500">{page.description}</p>
|
|
364
|
+
<div className="dv-prose mt-8">
|
|
365
|
+
<Body />
|
|
366
|
+
</div>
|
|
367
|
+
<div className="mt-14 grid gap-3 border-t border-slate-200 pt-8 sm:grid-cols-2">
|
|
368
|
+
{prev ? (
|
|
369
|
+
<Link to={docPath(prev)} className="group rounded-xl border border-slate-200 p-4 transition hover:border-brand">
|
|
370
|
+
<div className="flex items-center gap-1 text-xs text-slate-400">
|
|
371
|
+
<ChevronLeft className="h-3.5 w-3.5" /> Précédent
|
|
372
|
+
</div>
|
|
373
|
+
<div className="mt-1 font-semibold text-navy group-hover:text-brand-dark">{prev.title}</div>
|
|
374
|
+
</Link>
|
|
375
|
+
) : (
|
|
376
|
+
<span />
|
|
377
|
+
)}
|
|
378
|
+
{next && (
|
|
379
|
+
<Link to={docPath(next)} className="group rounded-xl border border-slate-200 p-4 text-right transition hover:border-brand">
|
|
380
|
+
<div className="flex items-center justify-end gap-1 text-xs text-slate-400">
|
|
381
|
+
Suivant <ChevronRight className="h-3.5 w-3.5" />
|
|
382
|
+
</div>
|
|
383
|
+
<div className="mt-1 font-semibold text-navy group-hover:text-brand-dark">{next.title}</div>
|
|
384
|
+
</Link>
|
|
385
|
+
)}
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
</article>
|
|
389
|
+
<aside className="sticky top-16 hidden h-[calc(100vh-4rem)] w-56 shrink-0 overflow-y-auto py-12 xl:block">
|
|
390
|
+
<Toc deps={page.slug} />
|
|
391
|
+
</aside>
|
|
392
|
+
</div>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* ------------------------------------------------------------------ Accueil */
|
|
397
|
+
|
|
398
|
+
const HERO_CODE = `import { definePublicModule } from '@snapify/sdk';
|
|
399
|
+
|
|
400
|
+
function Bubble({ settings, api }) {
|
|
401
|
+
return (
|
|
402
|
+
<button onClick={() => api.post('/hello')}>
|
|
403
|
+
{settings.label}
|
|
404
|
+
</button>
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
export default definePublicModule({
|
|
409
|
+
slots: { floating: Bubble },
|
|
410
|
+
});`;
|
|
411
|
+
|
|
412
|
+
function Card({ to, icon, title, children }: { to: string; icon: ReactNode; title: string; children: ReactNode }) {
|
|
413
|
+
return (
|
|
414
|
+
<Link to={to} className="group flex flex-col rounded-2xl border border-slate-200 bg-white p-6 transition hover:-translate-y-0.5 hover:border-brand hover:shadow-lg hover:shadow-teal-900/5">
|
|
415
|
+
<span className="flex h-11 w-11 items-center justify-center rounded-xl bg-teal-50 text-brand-dark ring-1 ring-teal-100">{icon}</span>
|
|
416
|
+
<span className="mt-5 text-lg font-semibold text-navy">{title}</span>
|
|
417
|
+
<span className="mt-2 flex-1 text-[15px] leading-relaxed text-slate-500">{children}</span>
|
|
418
|
+
<span className="mt-5 inline-flex items-center gap-1 text-sm font-semibold text-brand-dark">
|
|
419
|
+
Lire le guide <ArrowRight className="h-4 w-4 transition group-hover:translate-x-0.5" />
|
|
420
|
+
</span>
|
|
421
|
+
</Link>
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function Home() {
|
|
426
|
+
useEffect(() => {
|
|
427
|
+
document.title = 'Snapify Developers — Créer des modules et des thèmes';
|
|
428
|
+
}, []);
|
|
429
|
+
return (
|
|
430
|
+
<>
|
|
431
|
+
<section className="dv-grid-bg relative overflow-hidden bg-navy">
|
|
432
|
+
<div className="pointer-events-none absolute -top-40 right-[-10%] h-[520px] w-[520px] rounded-full bg-brand/20 blur-3xl" />
|
|
433
|
+
<div className="relative mx-auto grid max-w-6xl items-center gap-12 px-4 py-16 sm:px-6 lg:grid-cols-[1.1fr_1fr] lg:py-24">
|
|
434
|
+
<div>
|
|
435
|
+
<span className="inline-flex items-center gap-2 rounded-full border border-white/15 bg-white/5 px-3 py-1 text-xs font-medium text-teal-200">
|
|
436
|
+
<span className="h-1.5 w-1.5 rounded-full bg-brand" /> SDK Snapify · API {API_VERSION}
|
|
437
|
+
</span>
|
|
438
|
+
<h1 className="mt-6 text-4xl font-extrabold leading-[1.08] tracking-tight text-white sm:text-5xl lg:text-[3.4rem]">
|
|
439
|
+
Construisez pour
|
|
440
|
+
<br />
|
|
441
|
+
<span className="text-brand">le CMS tunisien.</span>
|
|
442
|
+
</h1>
|
|
443
|
+
<p className="mt-5 max-w-xl text-lg leading-relaxed text-slate-300">
|
|
444
|
+
Créez des modules et des thèmes en React et TypeScript, puis vendez-les à tous les sites Snapify depuis la marketplace.
|
|
445
|
+
</p>
|
|
446
|
+
<div className="mt-8 flex flex-wrap gap-3">
|
|
447
|
+
<Link to="/docs/demarrage-rapide" className="inline-flex h-11 items-center gap-2 rounded-lg bg-brand px-5 text-sm font-semibold text-navy transition hover:bg-teal-300">
|
|
448
|
+
Créer mon premier module <ArrowRight className="h-4 w-4" />
|
|
449
|
+
</Link>
|
|
450
|
+
<Link to="/docs/kit-developpeur" className="inline-flex h-11 items-center gap-2 rounded-lg border border-white/20 px-5 text-sm font-semibold text-white transition hover:bg-white/10">
|
|
451
|
+
<Download className="h-4 w-4" /> Installer le kit
|
|
452
|
+
</Link>
|
|
453
|
+
</div>
|
|
454
|
+
<div className="mt-6 flex flex-col items-start gap-4">
|
|
455
|
+
<div className="inline-flex items-center gap-3 rounded-lg border border-white/10 bg-white/5 px-4 py-2.5 font-mono text-sm text-slate-200">
|
|
456
|
+
<span className="select-none text-brand">$</span> npm create snapify@latest
|
|
457
|
+
</div>
|
|
458
|
+
<Link to="/docs/introduction" className="inline-flex items-center gap-2 text-sm font-medium text-slate-300 transition hover:text-white">
|
|
459
|
+
<BookOpen className="h-4 w-4" /> Lire la documentation
|
|
460
|
+
</Link>
|
|
461
|
+
</div>
|
|
462
|
+
</div>
|
|
463
|
+
<div className="min-w-0 lg:pl-6">
|
|
464
|
+
<Code lang="tsx" title="src/public.tsx">
|
|
465
|
+
{HERO_CODE}
|
|
466
|
+
</Code>
|
|
467
|
+
</div>
|
|
468
|
+
</div>
|
|
469
|
+
</section>
|
|
470
|
+
|
|
471
|
+
<section className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:py-20">
|
|
472
|
+
<h2 className="text-2xl font-bold tracking-tight text-navy sm:text-3xl">Par où commencer ?</h2>
|
|
473
|
+
<p className="mt-2 text-slate-500">Des guides pas à pas, du premier composant jusqu’à la publication.</p>
|
|
474
|
+
<div className="mt-8 grid gap-5 sm:grid-cols-2 lg:grid-cols-4">
|
|
475
|
+
<Card to="/docs/demarrage-rapide" icon={<Puzzle className="h-5 w-5" />} title="Modules">
|
|
476
|
+
Widgets, pages, API serveur, base de données et pages d’administration.
|
|
477
|
+
</Card>
|
|
478
|
+
<Card to="/docs/themes/creer-un-theme" icon={<LayoutTemplate className="h-5 w-5" />} title="Thèmes">
|
|
479
|
+
En-tête, pied de page et rendu des 26 blocs de l’éditeur, avec options personnalisables.
|
|
480
|
+
</Card>
|
|
481
|
+
<Card to="/docs/paquets" icon={<PackageCheck className="h-5 w-5" />} title="Paquets">
|
|
482
|
+
Construire un paquet, le vérifier, comprendre la signature et les licences.
|
|
483
|
+
</Card>
|
|
484
|
+
<Card to="/docs/publier" icon={<Store className="h-5 w-5" />} title="Marketplace">
|
|
485
|
+
Devenir développeur, publier, mettre à jour et suivre vos ventes.
|
|
486
|
+
</Card>
|
|
487
|
+
</div>
|
|
488
|
+
</section>
|
|
489
|
+
|
|
490
|
+
<section className="border-y border-slate-200 bg-slate-50">
|
|
491
|
+
<div className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:py-20">
|
|
492
|
+
<h2 className="text-2xl font-bold tracking-tight text-navy sm:text-3xl">Du code à la marketplace</h2>
|
|
493
|
+
<div className="mt-10 grid gap-8 md:grid-cols-4">
|
|
494
|
+
{[
|
|
495
|
+
{ icon: <FileCode2 className="h-5 w-5" />, t: 'Développer', d: <><code className="font-mono text-[13px]">npm create snapify</code> prépare le projet : un manifeste, des composants React, du TypeScript côté serveur.</> },
|
|
496
|
+
{ icon: <Boxes className="h-5 w-5" />, t: 'Empaqueter', d: <><code className="font-mono text-[13px]">npm run build</code> produit le <code className="font-mono text-[13px]">.snapmod</code> et rejoue l’analyse de la marketplace.</> },
|
|
497
|
+
{ icon: <ShieldCheck className="h-5 w-5" />, t: 'Revue et signature', d: 'NOVALABS relit votre code puis signe la version validée.' },
|
|
498
|
+
{ icon: <Store className="h-5 w-5" />, t: 'Publier', d: 'Vos clients l’installent en un clic depuis leur administration.' },
|
|
499
|
+
].map((s, i) => (
|
|
500
|
+
<div key={s.t}>
|
|
501
|
+
<div className="flex items-center gap-3">
|
|
502
|
+
<span className="flex h-10 w-10 items-center justify-center rounded-full bg-navy text-white">{s.icon}</span>
|
|
503
|
+
<span className="text-xs font-semibold text-slate-400">ÉTAPE {i + 1}</span>
|
|
504
|
+
</div>
|
|
505
|
+
<div className="mt-4 font-semibold text-navy">{s.t}</div>
|
|
506
|
+
<div className="mt-1 text-[15px] leading-relaxed text-slate-500">{s.d}</div>
|
|
507
|
+
</div>
|
|
508
|
+
))}
|
|
509
|
+
</div>
|
|
510
|
+
</div>
|
|
511
|
+
</section>
|
|
512
|
+
|
|
513
|
+
<section className="mx-auto max-w-6xl px-4 py-16 sm:px-6 lg:py-20">
|
|
514
|
+
<div className="grid items-center gap-10 lg:grid-cols-2">
|
|
515
|
+
<div>
|
|
516
|
+
<h2 className="text-2xl font-bold tracking-tight text-navy sm:text-3xl">Un contexte serveur sûr</h2>
|
|
517
|
+
<p className="mt-3 text-[15px] leading-relaxed text-slate-500">
|
|
518
|
+
Votre module déclare ce dont il a besoin ; le site lui donne exactement cela. Routes HTTP, collections, e-mails, appels vers une API
|
|
519
|
+
externe : chaque capacité est liée à une permission affichée au client avant l’installation.
|
|
520
|
+
</p>
|
|
521
|
+
<div className="mt-6 flex flex-wrap gap-2">
|
|
522
|
+
{['http:routes', 'database', 'storage', 'files', 'mail:send', 'admin:routes', 'net:api.exemple.tn'].map((p) => (
|
|
523
|
+
<span key={p} className="rounded-md bg-slate-100 px-2 py-1 font-mono text-xs text-slate-600">
|
|
524
|
+
{p}
|
|
525
|
+
</span>
|
|
526
|
+
))}
|
|
527
|
+
</div>
|
|
528
|
+
<Link to="/docs/modules/serveur" className="mt-6 inline-flex items-center gap-1 text-sm font-semibold text-brand-dark hover:underline">
|
|
529
|
+
Découvrir l’API serveur <ArrowRight className="h-4 w-4" />
|
|
530
|
+
</Link>
|
|
531
|
+
</div>
|
|
532
|
+
<div className="min-w-0">
|
|
533
|
+
<Code lang="ts" title="src/server.ts">{`ctx.http.post('/inscription', async (req) => {
|
|
534
|
+
const email = String(req.body?.email || '').trim();
|
|
535
|
+
if (!email.includes('@')) throw ctx.error(422, 'E-mail invalide');
|
|
536
|
+
|
|
537
|
+
const list = ctx.db.collection('inscrits');
|
|
538
|
+
await list.insert({ email });
|
|
539
|
+
|
|
540
|
+
const { html } = ctx.mail.render({ title: 'Bienvenue', intro: 'Merci !' });
|
|
541
|
+
await ctx.mail.send({ to: email, subject: 'Bienvenue', html });
|
|
542
|
+
return { ok: true };
|
|
543
|
+
});`}</Code>
|
|
544
|
+
</div>
|
|
545
|
+
</div>
|
|
546
|
+
</section>
|
|
547
|
+
|
|
548
|
+
<section className="mx-auto max-w-6xl px-4 pb-20 sm:px-6">
|
|
549
|
+
<div className="flex flex-col items-start justify-between gap-6 rounded-2xl bg-navy p-8 sm:flex-row sm:items-center sm:p-10">
|
|
550
|
+
<div>
|
|
551
|
+
<h2 className="text-2xl font-bold text-white">Prêt à publier ?</h2>
|
|
552
|
+
<p className="mt-2 max-w-xl text-slate-300">Ouvrez votre espace développeur sur la marketplace et proposez votre première extension.</p>
|
|
553
|
+
</div>
|
|
554
|
+
<div className="flex flex-wrap gap-3">
|
|
555
|
+
<a href={`${MARKETPLACE_URL}/developer`} className="inline-flex h-11 items-center rounded-lg bg-brand px-5 text-sm font-semibold text-navy hover:bg-teal-300">
|
|
556
|
+
Devenir développeur
|
|
557
|
+
</a>
|
|
558
|
+
<Link to="/docs/publier" className="inline-flex h-11 items-center rounded-lg border border-white/20 px-5 text-sm font-semibold text-white hover:bg-white/10">
|
|
559
|
+
Comment ça marche
|
|
560
|
+
</Link>
|
|
561
|
+
</div>
|
|
562
|
+
</div>
|
|
563
|
+
</section>
|
|
564
|
+
</>
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/* ------------------------------------------------------------------ Pied de page */
|
|
569
|
+
|
|
570
|
+
function Footer() {
|
|
571
|
+
return (
|
|
572
|
+
<footer className="border-t border-slate-200 bg-white">
|
|
573
|
+
<div className="mx-auto flex max-w-[1440px] flex-col gap-4 px-4 py-8 text-sm text-slate-500 sm:px-6 md:flex-row md:items-center md:justify-between">
|
|
574
|
+
<div className="flex items-center gap-2">
|
|
575
|
+
<span className="flex h-7 w-7 items-center justify-center rounded-md bg-navy">
|
|
576
|
+
<SnapifyMark className="h-5 w-5" />
|
|
577
|
+
</span>
|
|
578
|
+
© {new Date().getFullYear()} NOVALABS WEB DESIGN — Tunisie
|
|
579
|
+
</div>
|
|
580
|
+
<div className="flex flex-wrap gap-x-5 gap-y-2">
|
|
581
|
+
<a href={MARKETPLACE_URL} className="hover:text-navy">
|
|
582
|
+
Marketplace
|
|
583
|
+
</a>
|
|
584
|
+
<a href={COMMUNITY_URL} className="hover:text-navy">
|
|
585
|
+
Community
|
|
586
|
+
</a>
|
|
587
|
+
<a href={SITE_URL} className="hover:text-navy">
|
|
588
|
+
snapify.tn
|
|
589
|
+
</a>
|
|
590
|
+
<a href="https://snapify.fr" className="hover:text-navy">
|
|
591
|
+
snapify.fr
|
|
592
|
+
</a>
|
|
593
|
+
<a href="https://novalabs.tn" className="hover:text-navy" rel="noopener noreferrer">
|
|
594
|
+
NOVALABS
|
|
595
|
+
</a>
|
|
596
|
+
<a href={`mailto:${CONTACT_EMAIL}`} className="hover:text-navy">
|
|
597
|
+
{CONTACT_EMAIL}
|
|
598
|
+
</a>
|
|
599
|
+
</div>
|
|
600
|
+
</div>
|
|
601
|
+
</footer>
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/* ------------------------------------------------------------------ Application */
|
|
606
|
+
|
|
607
|
+
export function App() {
|
|
608
|
+
const [searchOpen, setSearchOpen] = useState(false);
|
|
609
|
+
const [menuOpen, setMenuOpen] = useState(false);
|
|
610
|
+
const { pathname } = useLocation();
|
|
611
|
+
|
|
612
|
+
useEffect(() => setMenuOpen(false), [pathname]);
|
|
613
|
+
useEffect(() => {
|
|
614
|
+
document.body.style.overflow = menuOpen ? 'hidden' : '';
|
|
615
|
+
}, [menuOpen]);
|
|
616
|
+
useEffect(() => {
|
|
617
|
+
const onKey = (e: KeyboardEvent) => {
|
|
618
|
+
if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {
|
|
619
|
+
e.preventDefault();
|
|
620
|
+
setSearchOpen(true);
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
window.addEventListener('keydown', onKey);
|
|
624
|
+
return () => window.removeEventListener('keydown', onKey);
|
|
625
|
+
}, []);
|
|
626
|
+
|
|
627
|
+
return (
|
|
628
|
+
<div className="flex min-h-screen flex-col">
|
|
629
|
+
<Header onSearch={() => setSearchOpen(true)} onMenu={() => setMenuOpen((o) => !o)} menuOpen={menuOpen} />
|
|
630
|
+
<MobileNav open={menuOpen} onClose={() => setMenuOpen(false)} />
|
|
631
|
+
<main className="flex-1">
|
|
632
|
+
<Routes>
|
|
633
|
+
<Route path="/" element={<Home />} />
|
|
634
|
+
<Route path="/docs" element={<Navigate to="/docs/introduction" replace />} />
|
|
635
|
+
<Route path="/docs/*" element={<DocView />} />
|
|
636
|
+
<Route path="*" element={<Navigate to="/" replace />} />
|
|
637
|
+
</Routes>
|
|
638
|
+
</main>
|
|
639
|
+
<Footer />
|
|
640
|
+
<SearchDialog open={searchOpen} onClose={() => setSearchOpen(false)} />
|
|
641
|
+
</div>
|
|
642
|
+
);
|
|
643
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Snapify Developers
|
|
3
|
+
* © 2026 NOVALABS WEB DESIGN — Tunisie
|
|
4
|
+
*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { createRoot } from 'react-dom/client';
|
|
7
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
8
|
+
import { App } from './App';
|
|
9
|
+
import './styles.css';
|
|
10
|
+
|
|
11
|
+
createRoot(document.getElementById('root')!).render(
|
|
12
|
+
<React.StrictMode>
|
|
13
|
+
<BrowserRouter>
|
|
14
|
+
<App />
|
|
15
|
+
</BrowserRouter>
|
|
16
|
+
</React.StrictMode>
|
|
17
|
+
);
|