create-snapify-app 0.0.0-stage → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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 +35 -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 +123 -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,529 @@
|
|
|
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 { toast } from 'sonner';
|
|
9
|
+
import { Check, CheckCircle2, ChevronDown, Copy, ExternalLink, FileJson, KeyRound, Lightbulb, ShieldCheck, Trash2 } from 'lucide-react';
|
|
10
|
+
import { api, type GoogleStatus } from '../lib/api';
|
|
11
|
+
import { errorMessage } from '../lib/hooks';
|
|
12
|
+
import { Alert, Badge, Button, Card, cx, Field, Input } from '../ui/primitives';
|
|
13
|
+
import { Modal, useConfirm } from '../ui/overlays';
|
|
14
|
+
|
|
15
|
+
const CONSOLE = {
|
|
16
|
+
project: 'https://console.cloud.google.com/projectcreate',
|
|
17
|
+
api: 'https://console.cloud.google.com/apis/library/searchconsole.googleapis.com',
|
|
18
|
+
branding: 'https://console.cloud.google.com/auth/branding',
|
|
19
|
+
audience: 'https://console.cloud.google.com/auth/audience',
|
|
20
|
+
client: 'https://console.cloud.google.com/auth/clients/create',
|
|
21
|
+
clients: 'https://console.cloud.google.com/auth/clients',
|
|
22
|
+
permissions: 'https://myaccount.google.com/connections',
|
|
23
|
+
searchConsole: 'https://search.google.com/search-console/welcome',
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Messages affichés au retour de Google (paramètre `reason`). */
|
|
27
|
+
export function googleErrorMessage(reason: string | null): string {
|
|
28
|
+
switch (reason) {
|
|
29
|
+
case 'access_denied':
|
|
30
|
+
return 'Google a refusé l’accès : soit vous avez cliqué sur « Annuler », soit l’application est encore en mode « Test » et ce compte Google n’est pas déclaré comme utilisateur test. Publiez l’application (étape 3) puis réessayez.';
|
|
31
|
+
case 'redirect_uri_mismatch':
|
|
32
|
+
return 'L’URI de redirection ne correspond pas à celle déclarée dans l’ID client Google. Recopiez-la exactement (étape 4).';
|
|
33
|
+
case 'invalid_client':
|
|
34
|
+
return 'Google ne reconnaît pas l’ID client ou le code secret. Vérifiez les identifiants enregistrés.';
|
|
35
|
+
case 'invalid_grant':
|
|
36
|
+
return 'Le code d’autorisation a expiré. Relancez la connexion.';
|
|
37
|
+
case 'no_refresh_token':
|
|
38
|
+
return 'Google n’a pas fourni d’accès permanent. Retirez l’application de votre compte Google (Sécurité › Applications tierces) puis reconnectez-vous.';
|
|
39
|
+
case 'scope_denied':
|
|
40
|
+
return 'L’accès à Search Console n’a pas été coché sur l’écran de Google. Relancez la connexion et cochez toutes les autorisations demandées.';
|
|
41
|
+
case 'state':
|
|
42
|
+
return 'La demande de connexion a expiré (plus de 10 minutes) ou le serveur a redémarré entre-temps. Relancez la connexion.';
|
|
43
|
+
case 'network':
|
|
44
|
+
return 'Le serveur n’a pas pu joindre Google. Vérifiez sa connexion Internet.';
|
|
45
|
+
default:
|
|
46
|
+
return 'La connexion à Google a échoué. Réessayez, puis consultez « Problèmes fréquents » si l’erreur persiste.';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function CopyRow({ label, value }: { label: string; value: string }) {
|
|
51
|
+
const [done, setDone] = useState(false);
|
|
52
|
+
return (
|
|
53
|
+
<div>
|
|
54
|
+
<p className="mb-1 text-xs font-medium text-slate-500">{label}</p>
|
|
55
|
+
<div className="flex gap-2">
|
|
56
|
+
<Input readOnly value={value} onFocus={(e) => e.target.select()} className="font-mono text-xs" />
|
|
57
|
+
<Button
|
|
58
|
+
icon={done ? Check : Copy}
|
|
59
|
+
onClick={() => {
|
|
60
|
+
void navigator.clipboard.writeText(value);
|
|
61
|
+
setDone(true);
|
|
62
|
+
setTimeout(() => setDone(false), 1500);
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{done ? 'Copié' : 'Copier'}
|
|
66
|
+
</Button>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const ConsoleLink = ({ href, children }: { href: string; children: React.ReactNode }) => (
|
|
73
|
+
<a href={href} target="_blank" rel="noreferrer">
|
|
74
|
+
<Button size="sm" icon={ExternalLink}>
|
|
75
|
+
{children}
|
|
76
|
+
</Button>
|
|
77
|
+
</a>
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
/** Formulaire ID client + code secret, avec import du fichier JSON téléchargé depuis Google. */
|
|
81
|
+
export function CredentialsForm({ status, onSaved, onCancel }: { status: GoogleStatus; onSaved: () => void; onCancel?: () => void }) {
|
|
82
|
+
const [clientId, setClientId] = useState(status.source === 'settings' ? status.clientId : '');
|
|
83
|
+
const [secret, setSecret] = useState('');
|
|
84
|
+
const [saving, setSaving] = useState(false);
|
|
85
|
+
const [error, setError] = useState<string | null>(null);
|
|
86
|
+
const keepSecret = status.source === 'settings' && clientId.trim() === status.clientId;
|
|
87
|
+
const idValid = /^[0-9]+-[a-z0-9]+\.apps\.googleusercontent\.com$/.test(clientId.trim());
|
|
88
|
+
|
|
89
|
+
const importJson = async (file: File) => {
|
|
90
|
+
try {
|
|
91
|
+
const json = JSON.parse(await file.text()) as { web?: { client_id?: string; client_secret?: string; redirect_uris?: string[] }; installed?: unknown };
|
|
92
|
+
if (!json.web) {
|
|
93
|
+
setError(json.installed ? 'Ce fichier correspond à une application de bureau : créez un ID client de type « Application Web ».' : 'Fichier non reconnu : téléchargez le JSON de l’ID client depuis la console Google.');
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
setClientId(json.web.client_id ?? '');
|
|
97
|
+
setSecret(json.web.client_secret ?? '');
|
|
98
|
+
setError(
|
|
99
|
+
json.web.redirect_uris && !json.web.redirect_uris.includes(status.redirectUri)
|
|
100
|
+
? `Attention : l’URI ${status.redirectUri} ne figure pas dans les URI de redirection de ce client. Ajoutez-la dans la console Google.`
|
|
101
|
+
: null
|
|
102
|
+
);
|
|
103
|
+
toast.success('Identifiants importés depuis le fichier');
|
|
104
|
+
} catch {
|
|
105
|
+
setError('Fichier JSON illisible.');
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const save = async () => {
|
|
110
|
+
setSaving(true);
|
|
111
|
+
setError(null);
|
|
112
|
+
try {
|
|
113
|
+
await api.google.saveCredentials(clientId.trim(), secret.trim());
|
|
114
|
+
toast.success('Identifiants vérifiés auprès de Google et enregistrés');
|
|
115
|
+
onSaved();
|
|
116
|
+
} catch (e) {
|
|
117
|
+
setError(errorMessage(e));
|
|
118
|
+
} finally {
|
|
119
|
+
setSaving(false);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<form
|
|
125
|
+
className="space-y-4"
|
|
126
|
+
onSubmit={(e) => {
|
|
127
|
+
e.preventDefault();
|
|
128
|
+
void save();
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
<label className="flex cursor-pointer items-center gap-3 rounded-lg border border-dashed border-slate-300 px-4 py-3 text-sm text-slate-600 transition hover:border-brand-400 hover:bg-brand-50/40">
|
|
132
|
+
<FileJson className="h-5 w-5 text-slate-400" />
|
|
133
|
+
<span>
|
|
134
|
+
<span className="font-medium text-slate-800">Importer le fichier JSON</span> téléchargé depuis Google (facultatif)
|
|
135
|
+
</span>
|
|
136
|
+
<input type="file" accept="application/json,.json" className="hidden" onChange={(e) => e.target.files?.[0] && void importJson(e.target.files[0]).then(() => (e.target.value = ''))} />
|
|
137
|
+
</label>
|
|
138
|
+
<Field label="ID client" hint="Se termine par .apps.googleusercontent.com">
|
|
139
|
+
<Input value={clientId} onChange={(e) => setClientId(e.target.value)} placeholder="123456789012-abc….apps.googleusercontent.com" className="font-mono text-xs" invalid={!!clientId && !idValid} autoComplete="off" spellCheck={false} />
|
|
140
|
+
</Field>
|
|
141
|
+
<Field label="Code secret du client" hint={keepSecret ? 'Laissez vide pour conserver le code secret enregistré.' : 'Commence généralement par GOCSPX-. Il est chiffré dans la base de données.'}>
|
|
142
|
+
<Input type="password" value={secret} onChange={(e) => setSecret(e.target.value)} placeholder={keepSecret ? '•••••••• (inchangé)' : 'GOCSPX-…'} className="font-mono text-xs" autoComplete="new-password" />
|
|
143
|
+
</Field>
|
|
144
|
+
{error ? <Alert tone="danger">{error}</Alert> : null}
|
|
145
|
+
<div className="flex justify-end gap-2">
|
|
146
|
+
{onCancel ? (
|
|
147
|
+
<Button type="button" onClick={onCancel}>
|
|
148
|
+
Annuler
|
|
149
|
+
</Button>
|
|
150
|
+
) : null}
|
|
151
|
+
<Button type="submit" variant="primary" icon={ShieldCheck} loading={saving} disabled={!idValid || (!secret.trim() && !keepSecret)}>
|
|
152
|
+
Vérifier et enregistrer
|
|
153
|
+
</Button>
|
|
154
|
+
</div>
|
|
155
|
+
</form>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const STEPS = ['project', 'api', 'consent', 'client', 'credentials'] as const;
|
|
160
|
+
type StepId = (typeof STEPS)[number];
|
|
161
|
+
const STORE_KEY = 'snapify.googleSetup';
|
|
162
|
+
|
|
163
|
+
function loadDone(): Set<StepId> {
|
|
164
|
+
try {
|
|
165
|
+
return new Set(JSON.parse(localStorage.getItem(STORE_KEY) || '[]') as StepId[]);
|
|
166
|
+
} catch {
|
|
167
|
+
return new Set();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Assistant pas à pas : projet Google Cloud, API, écran de consentement, ID client, saisie des identifiants. */
|
|
172
|
+
export function GoogleSetupWizard({ status, canManage, onConfigured }: { status: GoogleStatus; canManage: boolean; onConfigured: () => void }) {
|
|
173
|
+
const [done, setDone] = useState<Set<StepId>>(loadDone);
|
|
174
|
+
const firstOpen = STEPS.find((s) => !done.has(s)) ?? 'credentials';
|
|
175
|
+
const [open, setOpen] = useState<StepId>(firstOpen);
|
|
176
|
+
const isLocal = /^http:\/\/(localhost|127\.0\.0\.1)/.test(status.javascriptOrigin);
|
|
177
|
+
|
|
178
|
+
const mark = (id: StepId, value = true) => {
|
|
179
|
+
const next = new Set(done);
|
|
180
|
+
if (value) next.add(id);
|
|
181
|
+
else next.delete(id);
|
|
182
|
+
setDone(next);
|
|
183
|
+
localStorage.setItem(STORE_KEY, JSON.stringify([...next]));
|
|
184
|
+
if (value) {
|
|
185
|
+
const after = STEPS[STEPS.indexOf(id) + 1];
|
|
186
|
+
if (after) setOpen(after);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const steps: { id: StepId; title: string; time: string; body: React.ReactNode }[] = [
|
|
191
|
+
{
|
|
192
|
+
id: 'project',
|
|
193
|
+
title: 'Créer un projet Google Cloud',
|
|
194
|
+
time: '1 min',
|
|
195
|
+
body: (
|
|
196
|
+
<>
|
|
197
|
+
<p>
|
|
198
|
+
Connectez-vous avec le compte Google qui gère votre site dans Search Console, puis créez un projet. Nommez-le par exemple <strong>« Snapify – {new URL(status.publicUrl).hostname} »</strong>. Aucune
|
|
199
|
+
carte bancaire n’est demandée : l’API Search Console est gratuite.
|
|
200
|
+
</p>
|
|
201
|
+
<p className="text-slate-500">Vous avez déjà un projet (ou un projet Firebase) ? Vous pouvez le réutiliser : sélectionnez-le en haut de la console.</p>
|
|
202
|
+
<ConsoleLink href={CONSOLE.project}>Créer le projet</ConsoleLink>
|
|
203
|
+
</>
|
|
204
|
+
),
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
id: 'api',
|
|
208
|
+
title: 'Activer l’API Google Search Console',
|
|
209
|
+
time: '30 s',
|
|
210
|
+
body: (
|
|
211
|
+
<>
|
|
212
|
+
<p>
|
|
213
|
+
Vérifiez que votre projet est bien sélectionné en haut de la page, puis cliquez sur <strong>« Activer »</strong>.
|
|
214
|
+
</p>
|
|
215
|
+
<ConsoleLink href={CONSOLE.api}>Ouvrir l’API Search Console</ConsoleLink>
|
|
216
|
+
</>
|
|
217
|
+
),
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
id: 'consent',
|
|
221
|
+
title: 'Configurer l’écran de consentement',
|
|
222
|
+
time: '2 min',
|
|
223
|
+
body: (
|
|
224
|
+
<>
|
|
225
|
+
<ol className="list-decimal space-y-1.5 pl-5">
|
|
226
|
+
<li>
|
|
227
|
+
Cliquez sur <strong>« Commencer »</strong>. Nom de l’application : le nom de votre site ; e-mail d’assistance : le vôtre.
|
|
228
|
+
</li>
|
|
229
|
+
<li>
|
|
230
|
+
Audience : choisissez <strong>« Externe »</strong> (ou « Interne » si votre adresse est un compte Google Workspace de votre entreprise : vous pourrez alors ignorer le point 4).
|
|
231
|
+
</li>
|
|
232
|
+
<li>Indiquez votre e-mail de contact, acceptez les conditions et cliquez sur « Créer ».</li>
|
|
233
|
+
<li>
|
|
234
|
+
Ouvrez <strong>Audience</strong> et cliquez sur <strong>« Publier l’application »</strong> (état « En production »).
|
|
235
|
+
</li>
|
|
236
|
+
</ol>
|
|
237
|
+
<Alert tone="warning" icon={Lightbulb} title="Pourquoi publier ?">
|
|
238
|
+
En mode « Test », Google coupe l’accès au bout de 7 jours et il faudrait se reconnecter chaque semaine. Publier ne rend rien public et ne nécessite pas de validation par Google pour votre
|
|
239
|
+
propre usage : lors de la connexion, Google affichera seulement « Google n’a pas validé cette application » ; cliquez alors sur <strong>« Paramètres avancés »</strong> puis{' '}
|
|
240
|
+
<strong>« Accéder à … »</strong>.
|
|
241
|
+
</Alert>
|
|
242
|
+
<div className="flex flex-wrap gap-2">
|
|
243
|
+
<ConsoleLink href={CONSOLE.branding}>Écran de consentement</ConsoleLink>
|
|
244
|
+
<ConsoleLink href={CONSOLE.audience}>Audience (publier)</ConsoleLink>
|
|
245
|
+
</div>
|
|
246
|
+
</>
|
|
247
|
+
),
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
id: 'client',
|
|
251
|
+
title: 'Créer l’ID client OAuth',
|
|
252
|
+
time: '1 min',
|
|
253
|
+
body: (
|
|
254
|
+
<>
|
|
255
|
+
<ol className="list-decimal space-y-1.5 pl-5">
|
|
256
|
+
<li>
|
|
257
|
+
Type d’application : <strong>« Application Web »</strong> ; nom : « Snapify ».
|
|
258
|
+
</li>
|
|
259
|
+
<li>
|
|
260
|
+
Dans <strong>« Origines JavaScript autorisées »</strong>, ajoutez :
|
|
261
|
+
</li>
|
|
262
|
+
</ol>
|
|
263
|
+
<CopyRow label="Origine JavaScript autorisée" value={status.javascriptOrigin} />
|
|
264
|
+
<ol start={3} className="list-decimal space-y-1.5 pl-5">
|
|
265
|
+
<li>
|
|
266
|
+
Dans <strong>« URI de redirection autorisés »</strong>, ajoutez exactement :
|
|
267
|
+
</li>
|
|
268
|
+
</ol>
|
|
269
|
+
<CopyRow label="URI de redirection autorisé" value={status.redirectUri} />
|
|
270
|
+
<ol start={4} className="list-decimal space-y-1.5 pl-5">
|
|
271
|
+
<li>
|
|
272
|
+
Cliquez sur « Créer ». Google affiche l’<strong>ID client</strong> et le <strong>code secret</strong> : téléchargez le fichier JSON ou copiez les deux valeurs (le code secret ne sera
|
|
273
|
+
plus affiché en entier ensuite).
|
|
274
|
+
</li>
|
|
275
|
+
</ol>
|
|
276
|
+
{isLocal ? (
|
|
277
|
+
<Alert tone="info">
|
|
278
|
+
Vous êtes sur un poste local : ces adresses conviennent pour tester. Une fois le site en ligne, ajoutez au même ID client l’origine et l’URI de votre domaine (
|
|
279
|
+
<code className="font-mono text-xs">{new URL(status.publicUrl).origin}</code>), puis reconnectez le compte depuis l’administration en ligne.
|
|
280
|
+
</Alert>
|
|
281
|
+
) : null}
|
|
282
|
+
<ConsoleLink href={CONSOLE.client}>Créer l’ID client</ConsoleLink>
|
|
283
|
+
</>
|
|
284
|
+
),
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
id: 'credentials',
|
|
288
|
+
title: 'Coller les identifiants dans Snapify',
|
|
289
|
+
time: '30 s',
|
|
290
|
+
body: canManage ? (
|
|
291
|
+
<>
|
|
292
|
+
<p>Snapify vérifie les identifiants auprès de Google avant de les enregistrer.</p>
|
|
293
|
+
<CredentialsForm
|
|
294
|
+
status={status}
|
|
295
|
+
onSaved={() => {
|
|
296
|
+
mark('credentials');
|
|
297
|
+
onConfigured();
|
|
298
|
+
}}
|
|
299
|
+
/>
|
|
300
|
+
</>
|
|
301
|
+
) : (
|
|
302
|
+
<Alert tone="warning">Cette étape est réservée aux personnes autorisées à modifier les paramètres avancés. Transmettez l’ID client et le code secret à un administrateur.</Alert>
|
|
303
|
+
),
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
const completed = STEPS.filter((s) => done.has(s)).length;
|
|
308
|
+
|
|
309
|
+
return (
|
|
310
|
+
<Card padded={false}>
|
|
311
|
+
<div className="border-b border-slate-100 px-5 py-4">
|
|
312
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
313
|
+
<div>
|
|
314
|
+
<h2 className="text-[15px] font-semibold text-slate-900">Connecter Google Search Console</h2>
|
|
315
|
+
<p className="mt-0.5 max-w-2xl text-[13px] text-slate-500">
|
|
316
|
+
Comme Site Kit sur WordPress, Snapify affiche vos clics, impressions et positions Google. Il suffit de créer une fois un accès dans la console Google Cloud — comptez 5 minutes.
|
|
317
|
+
</p>
|
|
318
|
+
</div>
|
|
319
|
+
<Badge tone={completed === STEPS.length ? 'success' : 'neutral'}>
|
|
320
|
+
{completed} / {STEPS.length} étapes
|
|
321
|
+
</Badge>
|
|
322
|
+
</div>
|
|
323
|
+
<div className="mt-3 h-1.5 overflow-hidden rounded-full bg-slate-100">
|
|
324
|
+
<div className="h-full rounded-full bg-brand-500 transition-all" style={{ width: `${(completed / STEPS.length) * 100}%` }} />
|
|
325
|
+
</div>
|
|
326
|
+
<p className="mt-3 text-xs text-slate-500">
|
|
327
|
+
<strong className="font-medium text-slate-600">Firebase ou Google Cloud ?</strong> Inutile de passer par Firebase : tout se fait dans la console Google Cloud (un projet Firebase est de toute
|
|
328
|
+
façon un projet Google Cloud, réutilisable ici).
|
|
329
|
+
</p>
|
|
330
|
+
</div>
|
|
331
|
+
<ol>
|
|
332
|
+
{steps.map((s, i) => {
|
|
333
|
+
const isDone = done.has(s.id);
|
|
334
|
+
const isOpen = open === s.id;
|
|
335
|
+
return (
|
|
336
|
+
<li key={s.id} className="border-b border-slate-100 last:border-0">
|
|
337
|
+
<button type="button" onClick={() => setOpen(isOpen ? ('' as StepId) : s.id)} className="flex w-full items-center gap-3 px-5 py-3.5 text-left transition hover:bg-slate-50">
|
|
338
|
+
<span
|
|
339
|
+
className={cx(
|
|
340
|
+
'flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-xs font-semibold',
|
|
341
|
+
isDone ? 'bg-emerald-500 text-white' : isOpen ? 'bg-slate-900 text-white' : 'bg-slate-100 text-slate-500'
|
|
342
|
+
)}
|
|
343
|
+
>
|
|
344
|
+
{isDone ? <Check className="h-4 w-4" /> : i + 1}
|
|
345
|
+
</span>
|
|
346
|
+
<span className={cx('flex-1 text-sm font-medium', isDone ? 'text-slate-500' : 'text-slate-900')}>{s.title}</span>
|
|
347
|
+
<span className="text-xs text-slate-400">{s.time}</span>
|
|
348
|
+
<ChevronDown className={cx('h-4 w-4 text-slate-400 transition', isOpen && 'rotate-180')} />
|
|
349
|
+
</button>
|
|
350
|
+
{isOpen ? (
|
|
351
|
+
<div className="space-y-3 px-5 pb-5 pl-[60px] text-sm text-slate-600">
|
|
352
|
+
{s.body}
|
|
353
|
+
{s.id !== 'credentials' ? (
|
|
354
|
+
<div className="pt-1">
|
|
355
|
+
<Button size="sm" variant={isDone ? 'ghost' : 'primary'} icon={isDone ? undefined : CheckCircle2} onClick={() => mark(s.id, !isDone)}>
|
|
356
|
+
{isDone ? 'Marquer comme non fait' : 'C’est fait, étape suivante'}
|
|
357
|
+
</Button>
|
|
358
|
+
</div>
|
|
359
|
+
) : null}
|
|
360
|
+
</div>
|
|
361
|
+
) : null}
|
|
362
|
+
</li>
|
|
363
|
+
);
|
|
364
|
+
})}
|
|
365
|
+
</ol>
|
|
366
|
+
</Card>
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Identifiants en place : origine, modification, suppression. */
|
|
371
|
+
export function CredentialsCard({ status, canManage, onChanged }: { status: GoogleStatus; canManage: boolean; onChanged: () => void }) {
|
|
372
|
+
const confirm = useConfirm();
|
|
373
|
+
const [editing, setEditing] = useState(false);
|
|
374
|
+
return (
|
|
375
|
+
<Card className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
376
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
377
|
+
<span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-slate-100 text-slate-500">
|
|
378
|
+
<KeyRound className="h-4 w-4" />
|
|
379
|
+
</span>
|
|
380
|
+
<div className="min-w-0">
|
|
381
|
+
<p className="text-sm font-medium text-slate-900">Identifiants OAuth Google</p>
|
|
382
|
+
<p className="truncate font-mono text-xs text-slate-500">{status.clientId}</p>
|
|
383
|
+
<p className="text-xs text-slate-400">{status.source === 'env' ? 'Définis dans le fichier .env du serveur' : 'Enregistrés dans l’administration (code secret chiffré)'}</p>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
{canManage ? (
|
|
387
|
+
<div className="flex shrink-0 gap-2">
|
|
388
|
+
<Button size="sm" onClick={() => setEditing(true)}>
|
|
389
|
+
{status.source === 'env' ? 'Remplacer' : 'Modifier'}
|
|
390
|
+
</Button>
|
|
391
|
+
{status.source === 'settings' ? (
|
|
392
|
+
<Button
|
|
393
|
+
size="sm"
|
|
394
|
+
variant="ghost"
|
|
395
|
+
icon={Trash2}
|
|
396
|
+
className="text-rose-600 hover:bg-rose-50"
|
|
397
|
+
onClick={async () => {
|
|
398
|
+
if (
|
|
399
|
+
!(await confirm({
|
|
400
|
+
title: 'Supprimer les identifiants Google ?',
|
|
401
|
+
message: 'Le compte Google sera déconnecté et les données Search Console ne seront plus affichées.',
|
|
402
|
+
confirmLabel: 'Supprimer',
|
|
403
|
+
danger: true,
|
|
404
|
+
}))
|
|
405
|
+
)
|
|
406
|
+
return;
|
|
407
|
+
try {
|
|
408
|
+
await api.google.deleteCredentials();
|
|
409
|
+
toast.success('Identifiants supprimés');
|
|
410
|
+
onChanged();
|
|
411
|
+
} catch (e) {
|
|
412
|
+
toast.error(errorMessage(e));
|
|
413
|
+
}
|
|
414
|
+
}}
|
|
415
|
+
>
|
|
416
|
+
Supprimer
|
|
417
|
+
</Button>
|
|
418
|
+
) : null}
|
|
419
|
+
</div>
|
|
420
|
+
) : null}
|
|
421
|
+
<Modal open={editing} onClose={() => setEditing(false)} title="Identifiants OAuth Google" description="Changer d’ID client déconnecte le compte Google : il faudra vous reconnecter.">
|
|
422
|
+
<div className="space-y-4">
|
|
423
|
+
<CopyRow label="URI de redirection à déclarer dans l’ID client" value={status.redirectUri} />
|
|
424
|
+
<CredentialsForm
|
|
425
|
+
status={status}
|
|
426
|
+
onCancel={() => setEditing(false)}
|
|
427
|
+
onSaved={() => {
|
|
428
|
+
setEditing(false);
|
|
429
|
+
onChanged();
|
|
430
|
+
}}
|
|
431
|
+
/>
|
|
432
|
+
</div>
|
|
433
|
+
</Modal>
|
|
434
|
+
</Card>
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const PROBLEMS: { q: string; a: React.ReactNode }[] = [
|
|
439
|
+
{
|
|
440
|
+
q: '« Erreur 400 : redirect_uri_mismatch »',
|
|
441
|
+
a: 'L’URI de redirection déclarée dans l’ID client ne correspond pas exactement à celle de Snapify (http/https, port, barre finale). Recopiez-la depuis l’étape 4, enregistrez dans la console, attendez une minute et réessayez.',
|
|
442
|
+
},
|
|
443
|
+
{
|
|
444
|
+
q: '« Accès bloqué : l’application n’a pas terminé la procédure de validation »',
|
|
445
|
+
a: 'L’application est en mode « Test » et votre compte n’est pas déclaré comme utilisateur test. Publiez-la (étape 3, Audience › « Publier l’application ») ou ajoutez votre adresse dans « Utilisateurs test ».',
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
q: '« Google n’a pas validé cette application »',
|
|
449
|
+
a: 'C’est normal pour une application que vous utilisez vous-même : cliquez sur « Paramètres avancés » puis « Accéder à … (non sécurisé) ». Aucune validation Google n’est nécessaire.',
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
q: 'Il faut se reconnecter chaque semaine',
|
|
453
|
+
a: 'L’application est restée en mode « Test » : Google y limite les autorisations à 7 jours. Publiez-la (étape 3) puis reconnectez le compte une dernière fois.',
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
q: '« L’API n’est pas activée »',
|
|
457
|
+
a: (
|
|
458
|
+
<>
|
|
459
|
+
Activez l’API Google Search Console dans le même projet que l’ID client (étape 2), puis patientez une à deux minutes.{' '}
|
|
460
|
+
<a href={CONSOLE.api} target="_blank" rel="noreferrer" className="font-medium text-brand-700 hover:underline">
|
|
461
|
+
Ouvrir l’API
|
|
462
|
+
</a>
|
|
463
|
+
</>
|
|
464
|
+
),
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
q: 'Aucune propriété dans la liste',
|
|
468
|
+
a: 'Le compte Google connecté n’a pas accès au site dans Search Console. Ajoutez le site dans Search Console avec ce compte (ou demandez au propriétaire de vous y ajouter), puis rechargez la page.',
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
q: 'J’ai perdu le code secret',
|
|
472
|
+
a: (
|
|
473
|
+
<>
|
|
474
|
+
Dans la liste des{' '}
|
|
475
|
+
<a href={CONSOLE.clients} target="_blank" rel="noreferrer" className="font-medium text-brand-700 hover:underline">
|
|
476
|
+
ID clients
|
|
477
|
+
</a>
|
|
478
|
+
, ouvrez le client « Snapify » et ajoutez un nouveau code secret, puis enregistrez-le ici avec « Modifier ».
|
|
479
|
+
</>
|
|
480
|
+
),
|
|
481
|
+
},
|
|
482
|
+
];
|
|
483
|
+
|
|
484
|
+
export function GoogleTroubleshooting() {
|
|
485
|
+
const [open, setOpen] = useState<number | null>(null);
|
|
486
|
+
return (
|
|
487
|
+
<Card padded={false}>
|
|
488
|
+
<div className="border-b border-slate-100 px-5 py-4">
|
|
489
|
+
<h2 className="text-[15px] font-semibold text-slate-900">Problèmes fréquents</h2>
|
|
490
|
+
</div>
|
|
491
|
+
<ul className="divide-y divide-slate-100">
|
|
492
|
+
{PROBLEMS.map((p, i) => (
|
|
493
|
+
<li key={p.q}>
|
|
494
|
+
<button type="button" onClick={() => setOpen(open === i ? null : i)} className="flex w-full items-center gap-3 px-5 py-3 text-left text-sm text-slate-800 transition hover:bg-slate-50">
|
|
495
|
+
<span className="flex-1">{p.q}</span>
|
|
496
|
+
<ChevronDown className={cx('h-4 w-4 shrink-0 text-slate-400 transition', open === i && 'rotate-180')} />
|
|
497
|
+
</button>
|
|
498
|
+
{open === i ? <p className="px-5 pb-4 text-[13px] leading-relaxed text-slate-600">{p.a}</p> : null}
|
|
499
|
+
</li>
|
|
500
|
+
))}
|
|
501
|
+
</ul>
|
|
502
|
+
</Card>
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/** Aide quand le compte connecté ne voit aucune propriété Search Console. */
|
|
507
|
+
export function NoPropertyHelp({ status, onOpenSettings }: { status: GoogleStatus; onOpenSettings: () => void }) {
|
|
508
|
+
return (
|
|
509
|
+
<Alert tone="info" title="Ajoutez votre site dans Search Console">
|
|
510
|
+
<ol className="mt-1 list-decimal space-y-1 pl-5">
|
|
511
|
+
<li>
|
|
512
|
+
Ouvrez{' '}
|
|
513
|
+
<a href={CONSOLE.searchConsole} target="_blank" rel="noreferrer" className="font-medium underline">
|
|
514
|
+
Google Search Console
|
|
515
|
+
</a>{' '}
|
|
516
|
+
avec le compte {status.accountEmail ? <strong>{status.accountEmail}</strong> : 'connecté'} et choisissez « Préfixe de l’URL » : <code className="font-mono text-xs">{status.publicUrl}</code>.
|
|
517
|
+
</li>
|
|
518
|
+
<li>
|
|
519
|
+
Méthode « Balise HTML » : copiez le code proposé et collez-le dans{' '}
|
|
520
|
+
<button type="button" onClick={onOpenSettings} className="font-medium underline">
|
|
521
|
+
Réglages globaux › Google Search Console
|
|
522
|
+
</button>
|
|
523
|
+
{status.verificationTag ? ' (déjà renseigné)' : ''}, enregistrez, puis cliquez sur « Valider » dans Search Console.
|
|
524
|
+
</li>
|
|
525
|
+
<li>Revenez ici et rechargez la page : la propriété apparaîtra dans la liste.</li>
|
|
526
|
+
</ol>
|
|
527
|
+
</Alert>
|
|
528
|
+
);
|
|
529
|
+
}
|