diff --git a/.kontinuous/env/dev/values.yaml b/.kontinuous/env/dev/values.yaml index da2c5705..ecdf5f42 100644 --- a/.kontinuous/env/dev/values.yaml +++ b/.kontinuous/env/dev/values.yaml @@ -16,7 +16,7 @@ jobs: checkout: false shell: sh image: "{{ .Values.global.registry }}{{ if .Values.global.imageProject }}{{ print `/` .Values.global.imageProject }}{{ end }}/{{ .Values.global.imageRepository }}/app:{{ .Values.global.imageTag }}" - run: "yarn seed" + run: "yarn payload migrate && yarn seed:prod" envFrom: - secretRef: name: pg-app diff --git a/README.md b/README.md index 74b17bf0..0e17c7fa 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,14 @@ ## Developpement +## Base de donnée + +Lancez la base de donnée Postgres via docker compose : + +```bash +docker compose up -d +``` + ### Webapp Accédez au dossier de l'application NextJS webapp : @@ -27,7 +35,7 @@ yarn Générez les données de test : ```bash -yarn seed +yarn seed:dev ``` Lancez l'application, qui sera accessible sur le port 3000 : @@ -35,3 +43,9 @@ Lancez l'application, qui sera accessible sur le port 3000 : ```bash yarn dev ``` + +Voici les informations des utilisateurs prêts à être utilisés en développement grâce aux données de test : +| Email | Type de compte | Mot de passe | +| -------- | -------- | -------- | +| user@test.loc | Utilisateur | user123 | +| admin@test.loc | Administrateur | admin123 | diff --git a/imports/coupons.csv b/imports/coupons.csv new file mode 100644 index 00000000..40752c11 --- /dev/null +++ b/imports/coupons.csv @@ -0,0 +1,23 @@ +code,validityTo +H1D1,01/06/24 +H1D2,02/06/24 +H1D3,03/06/24 +H1D4,04/06/24 +H1D5,05/06/24 +H1D6,06/06/24 +H1D7,07/06/24 +H1D8,08/06/24 +H1D9,09/06/24 +H1D10,10/06/24 +H1D11,11/06/24 +H1D12,12/06/24 +H1D13,13/06/24 +H1D14,14/06/24 +H1D15,15/06/24 +H1D16,16/06/24 +H1D17,17/06/24 +H1D18,18/06/24 +H1D19,19/06/24 +H1D20,20/06/24 +H1D21,21/06/24 +H1D22,22/06/24 diff --git a/webapp/.env.example b/webapp/.env.example index 2686ec17..24bd01a5 100644 --- a/webapp/.env.example +++ b/webapp/.env.example @@ -1,4 +1,4 @@ -DATABASE_URL=postgres://user:password@localhost:5432/dbname +DATABASE_URL=postgres://user:password@localhost:5433/carte-jeune-engage PAYLOAD_SECRET=SOME_SECRET PAYLOAD_CONFIG_PATH=payload/payload.config.ts NEXT_PUBLIC_JWT_NAME=cje-jwt diff --git a/webapp/package.json b/webapp/package.json index fa9e8392..5d1b8371 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -6,19 +6,23 @@ "dev": "next dev", "build": "next build", "start": "next start", - "seed": "PAYLOAD_DROP_DATABASE=true tsx ./src/payload/seed/index.ts", + "seed:dev": "PAYLOAD_DROP_DATABASE=true tsx ./src/payload/seed/index.ts", + "seed:prod": "tsx ./src/payload/seed/index.ts", + "payload": "PAYLOAD_CONFIG_PATH=./src/payload/payload.config.ts payload", "lint": "next lint" }, "dependencies": { "@aws-sdk/client-s3": "^3.490.0", "@aws-sdk/lib-storage": "^3.490.0", + "@chakra-ui/icons": "^2.1.1", "@chakra-ui/next-js": "^2.2.0", "@chakra-ui/react": "^2.8.2", "@ducanh2912/next-pwa": "^10.1.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", + "@gsap/react": "^2.1.0", "@payloadcms/bundler-webpack": "^1.0.5", - "@payloadcms/db-postgres": "^0.3.0", + "@payloadcms/db-postgres": "^0.4.0", "@payloadcms/next-payload": "^0.1.11", "@payloadcms/plugin-cloud-storage": "^1.1.1", "@payloadcms/richtext-slate": "^1.3.1", @@ -32,9 +36,12 @@ "cookies-next": "^4.1.0", "dotenv": "^16.3.1", "framer-motion": "^10.16.16", + "gsap": "^3.12.5", + "ignore-styles": "^5.0.1", "jwt-decode": "^4.0.0", "next": "13.5.5", - "payload": "^2.6.0", + "papaparse": "^5.4.1", + "payload": "^2.8.2", "react": "^18", "react-dom": "^18", "react-hook-form": "^7.49.2", @@ -43,11 +50,11 @@ "superjson": "^1.13.3", "tsx": "^4.7.0", "usehooks-ts": "^2.9.2", - "zod": "^3.22.4", - "ignore-styles": "^5.0.1" + "zod": "^3.22.4" }, "devDependencies": { "@types/node": "^20", + "@types/papaparse": "^5", "@types/react": "^18", "@types/react-dom": "^18", "typescript": "^5", diff --git a/webapp/public/images/seeds/categories/bank.png b/webapp/public/images/seeds/categories/bank.png new file mode 100644 index 00000000..a1f2f57e Binary files /dev/null and b/webapp/public/images/seeds/categories/bank.png differ diff --git a/webapp/public/images/seeds/categories/care.svg b/webapp/public/images/seeds/categories/care.svg deleted file mode 100644 index 7050734a..00000000 --- a/webapp/public/images/seeds/categories/care.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/webapp/public/images/seeds/categories/equipment.png b/webapp/public/images/seeds/categories/equipment.png new file mode 100644 index 00000000..4a82ab56 Binary files /dev/null and b/webapp/public/images/seeds/categories/equipment.png differ diff --git a/webapp/public/images/seeds/categories/hobby.png b/webapp/public/images/seeds/categories/hobby.png new file mode 100644 index 00000000..4af75708 Binary files /dev/null and b/webapp/public/images/seeds/categories/hobby.png differ diff --git a/webapp/public/images/seeds/categories/hosting.png b/webapp/public/images/seeds/categories/hosting.png new file mode 100644 index 00000000..23b52953 Binary files /dev/null and b/webapp/public/images/seeds/categories/hosting.png differ diff --git a/webapp/public/images/seeds/categories/hygiene.png b/webapp/public/images/seeds/categories/hygiene.png new file mode 100644 index 00000000..016ebdfd Binary files /dev/null and b/webapp/public/images/seeds/categories/hygiene.png differ diff --git a/webapp/public/images/seeds/categories/mobility.png b/webapp/public/images/seeds/categories/mobility.png index f47424b3..85a96dc5 100644 Binary files a/webapp/public/images/seeds/categories/mobility.png and b/webapp/public/images/seeds/categories/mobility.png differ diff --git a/webapp/public/images/seeds/categories/services.svg b/webapp/public/images/seeds/categories/services.svg deleted file mode 100644 index 861746fd..00000000 --- a/webapp/public/images/seeds/categories/services.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/webapp/public/images/seeds/categories/shop.png b/webapp/public/images/seeds/categories/shop.png new file mode 100644 index 00000000..38660cac Binary files /dev/null and b/webapp/public/images/seeds/categories/shop.png differ diff --git a/webapp/public/images/seeds/categories/shop.svg b/webapp/public/images/seeds/categories/shop.svg deleted file mode 100644 index cf569220..00000000 --- a/webapp/public/images/seeds/categories/shop.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/webapp/public/images/seeds/categories/sport.png b/webapp/public/images/seeds/categories/sport.png new file mode 100644 index 00000000..2dbe3e74 Binary files /dev/null and b/webapp/public/images/seeds/categories/sport.png differ diff --git a/webapp/public/images/seeds/categories/telephony.png b/webapp/public/images/seeds/categories/telephony.png new file mode 100644 index 00000000..5fe74d66 Binary files /dev/null and b/webapp/public/images/seeds/categories/telephony.png differ diff --git a/webapp/public/images/seeds/categories/telephony.svg b/webapp/public/images/seeds/categories/telephony.svg deleted file mode 100644 index 42013d1e..00000000 --- a/webapp/public/images/seeds/categories/telephony.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/webapp/public/images/seeds/partners/cora.svg b/webapp/public/images/seeds/partners/cora.svg new file mode 100644 index 00000000..905b8787 --- /dev/null +++ b/webapp/public/images/seeds/partners/cora.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/webapp/public/pwa/manifest.json b/webapp/public/pwa/manifest.json index 93305199..80e2b31a 100644 --- a/webapp/public/pwa/manifest.json +++ b/webapp/public/pwa/manifest.json @@ -50,8 +50,8 @@ "purpose": "any" } ], - "theme_color": "#F7F7FA", - "background_color": "#F7F7FA", + "theme_color": "#F7F7F7", + "background_color": "#F7F7F7", "start_url": "/", "display": "standalone", "orientation": "portrait" diff --git a/webapp/src/components/InstallationBanner.tsx b/webapp/src/components/InstallationBanner.tsx index fbbd6aa3..86eabbf4 100644 --- a/webapp/src/components/InstallationBanner.tsx +++ b/webapp/src/components/InstallationBanner.tsx @@ -1,7 +1,8 @@ import React, { useEffect, useState } from "react"; -import { Button, Flex, Icon, Text, useToast } from "@chakra-ui/react"; +import { Box, Button, Flex, Icon, Text, useToast } from "@chakra-ui/react"; import { useAuth } from "~/providers/Auth"; import { useLocalStorage } from "usehooks-ts"; +import { CloseIcon } from "@chakra-ui/icons"; interface BeforeInstallPromptEvent extends Event { readonly platforms: Array; @@ -81,7 +82,7 @@ const InstallationBanner: React.FC = () => { return null; return ( - { p={4} borderRadius={8} bgColor="primary.500" - alignItems="center" - justifyContent="space-between" > - - - Installer l'application - - - Pour une meilleure expérience, installez l'app sur votre téléphone. - + + + + Installer l'application + + + Pour une meilleure expérience, installez l'app sur votre téléphone. + + + + setUserOutcome("dismissed")} + /> - - + ); }; diff --git a/webapp/src/components/LoadingLoader.tsx b/webapp/src/components/LoadingLoader.tsx new file mode 100644 index 00000000..61b262fd --- /dev/null +++ b/webapp/src/components/LoadingLoader.tsx @@ -0,0 +1,13 @@ +import { Spinner } from "@chakra-ui/react"; + +export default function LoadingLoader() { + return ( + + ); +} diff --git a/webapp/src/components/OfferKindBadge.tsx b/webapp/src/components/OfferKindBadge.tsx index 346f6d4a..e4d3aa75 100644 --- a/webapp/src/components/OfferKindBadge.tsx +++ b/webapp/src/components/OfferKindBadge.tsx @@ -6,18 +6,19 @@ import { OnlineIcon } from "~/components/icons/online"; export const OfferKindBadge = ({ kind, variant, + chakraProps, }: { kind: Offer["kind"]; variant: "light" | "dark"; + chakraProps?: Record; }) => { return ( {kind === "voucher" ? ( + {text} + + + ); +} diff --git a/webapp/src/components/icons/coupon.tsx b/webapp/src/components/icons/coupon.tsx new file mode 100644 index 00000000..7b54d19e --- /dev/null +++ b/webapp/src/components/icons/coupon.tsx @@ -0,0 +1,23 @@ +import { Icon, IconProps } from "@chakra-ui/react"; + +export const CouponIcon = (props: IconProps) => { + return ( + + + + + ); +}; diff --git a/webapp/src/components/modals/OfferActivationModal.tsx b/webapp/src/components/modals/OfferActivationModal.tsx new file mode 100644 index 00000000..2dffe152 --- /dev/null +++ b/webapp/src/components/modals/OfferActivationModal.tsx @@ -0,0 +1,79 @@ +import { ArrowForwardIcon } from '@chakra-ui/icons'; +import { + Button, + Text, + List, + ListIcon, + ListItem, + ModalBody, + ModalContent, + ModalHeader +} from '@chakra-ui/react'; +import { FiClock, FiGlobe, FiRotateCw, FiTag } from 'react-icons/fi'; +import { IconType } from 'react-icons/lib'; +import { TbBuildingStore } from 'react-icons/tb'; +import { OfferIncluded } from '~/server/api/routers/offer'; + +const OfferActivationModal = ({ + onClose, + onlyCgu, + offer, + mutateCouponToUser +}: { + onClose: () => void; + onlyCgu?: boolean; + offer: OfferIncluded; + mutateCouponToUser: ({ offer_id }: { offer_id: number }) => void; +}) => { + const validityToDate = new Date(offer.validityTo); + + const cguItems: { icon: IconType; text: string; cross?: boolean }[] = [ + { icon: FiGlobe, text: 'Utilisable en ligne' }, + { icon: TbBuildingStore, text: 'À utiliser en magasin', cross: true }, + { + icon: FiClock, + text: `À utiliser avant le ${validityToDate.toLocaleDateString()} !` + }, + { icon: FiRotateCw, text: 'Utilisation illimité' }, + { icon: FiTag, text: 'Non cumulable' } + ]; + + return ( + + {onlyCgu && Conditions d’utilisation} + + + {cguItems.map(({ icon, text, cross }, index) => ( + + + + {text} + + + ))} + + + + + ); +}; + +export default OfferActivationModal; diff --git a/webapp/src/components/wrappers/CategoriesWrapper.tsx b/webapp/src/components/wrappers/CategoriesWrapper.tsx new file mode 100644 index 00000000..14972e6a --- /dev/null +++ b/webapp/src/components/wrappers/CategoriesWrapper.tsx @@ -0,0 +1,49 @@ +import { ArrowBackIcon } from '@chakra-ui/icons'; +import { Button, Flex, Heading, SimpleGrid } from '@chakra-ui/react'; +import { useRouter } from 'next/router'; +import { ReactNode } from 'react'; + +type CategoriesWrapperProps = { + children: ReactNode; + isLoading?: boolean; +}; + +const CategoriesWrapper = ({ children, isLoading }: CategoriesWrapperProps) => { + const router = useRouter(); + + return ( + + + + + + ) : ( + + + + + )} + + + + + {offer && ( + + )} + + + ); +} diff --git a/webapp/src/pages/index.tsx b/webapp/src/pages/index.tsx index 07bbd335..dc78dbfe 100644 --- a/webapp/src/pages/index.tsx +++ b/webapp/src/pages/index.tsx @@ -1,108 +1,86 @@ -import { - AspectRatio, - Box, - Button, - Flex, - Heading, - Icon, - Image, - Text, -} from "@chakra-ui/react"; -import { useRouter } from "next/router"; -import { useState } from "react"; +import { api } from "~/utils/api"; +import { Box, Button, Flex, Heading, Icon } from "@chakra-ui/react"; +import { useForm, type SubmitHandler } from "react-hook-form"; +import FormInput from "~/components/FormInput"; import { HiOutlineArrowLeft, HiOutlineArrowRight } from "react-icons/hi"; +import { useRouter } from "next/router"; +import { setCookie } from "cookies-next"; + +type LoginForm = { + email: string; + password: string; +}; export default function Home() { const router = useRouter(); - const onBoardingItems = [ - { - title: "Des réductions pour vous accompagner au quotidien", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt", - image: "/images/onboarding/discount.svg", - }, - { - title: "Générer facilement la promo associé à l’offre", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt", - image: "/images/onboarding/box.svg", - }, - { - title: - "Utilisez la promo en ligne ou directement chez l’enseigne partenaire", - description: - "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt", - image: "/images/onboarding/computer.svg", + const { + handleSubmit, + register, + formState: { errors }, + } = useForm(); + + const { mutate: loginUser, isLoading } = api.user.login.useMutation({ + onSuccess: async ({ data }) => { + setCookie( + process.env.NEXT_PUBLIC_JWT_NAME ?? "cje-jwt", + data.token || "" + ); + router.reload(); + router.push("/dashboard"); }, - ]; + }); - const [onBoardingItemIndex, setOnBoardingItemIndex] = useState(0); + const handleLogin: SubmitHandler = async (values) => { + await loginUser(values); + }; return ( - - {onBoardingItems[onBoardingItemIndex]?.title} - + + + Connexion - - {onBoardingItems[onBoardingItemIndex]?.title} - - - {onBoardingItems[onBoardingItemIndex]?.description} - - - - { - if (onBoardingItemIndex !== 0) - setOnBoardingItemIndex((prev) => prev - 1); - }} - color={onBoardingItemIndex === 0 ? "gray.300" : "primary.500"} - /> - - { - if (onBoardingItemIndex !== onBoardingItems.length - 1) { - setOnBoardingItemIndex((prev) => prev + 1); - } else { - router.push("/login"); - } - }} - color="primary.500" - /> - - + + + + + ); } diff --git a/webapp/src/pages/login/index.tsx b/webapp/src/pages/login/index.tsx deleted file mode 100644 index 8ccf9642..00000000 --- a/webapp/src/pages/login/index.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { api } from "~/utils/api"; -import { Box, Button, Flex, Heading, Icon } from "@chakra-ui/react"; -import { useForm, type SubmitHandler } from "react-hook-form"; -import FormInput from "~/components/FormInput"; -import { HiOutlineArrowLeft, HiOutlineArrowRight } from "react-icons/hi"; -import { useRouter } from "next/router"; -import { setCookie } from "cookies-next"; - -type LoginForm = { - email: string; - password: string; -}; - -export default function Home() { - const router = useRouter(); - - const { - handleSubmit, - register, - formState: { errors }, - } = useForm(); - - const { mutate: loginUser, isLoading } = api.user.login.useMutation({ - onSuccess: async ({ data }) => { - setCookie(process.env.NEXT_PUBLIC_JWT_NAME as string, data.token || ""); - router.reload(); - router.push("/dashboard"); - }, - }); - - const handleLogin: SubmitHandler = async (values) => { - await loginUser(values); - }; - - return ( - - - Connexion - - - - - - - - - - ); -} diff --git a/webapp/src/pages/register/index.tsx b/webapp/src/pages/register/index.tsx deleted file mode 100644 index 05e1a68a..00000000 --- a/webapp/src/pages/register/index.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { api } from "~/utils/api"; -import { - Box, - Button, - Flex, - FormControl, - FormErrorMessage, - FormLabel, - Heading, - Input, - useToast, -} from "@chakra-ui/react"; -import { useForm, type SubmitHandler } from "react-hook-form"; -import { useRouter } from "next/router"; - -type RegisterForm = { - email: string; - firstName: string; - lastName: string; - password: string; - passwordConfirmation: string; -}; - -export default function Home() { - const router = useRouter(); - const toast = useToast(); - - const { - handleSubmit, - register, - setError, - getValues, - formState: { errors }, - } = useForm(); - - const { mutate: registerUser, isLoading } = api.user.register.useMutation({ - onSuccess: () => { - toast({ - title: "Inscription réussie", - description: "Vous pouvez maintenant vous connecter", - status: "success", - duration: 5000, - isClosable: true, - }); - router.push("/login"); - }, - onError: (error) => { - console.log(error.data?.httpStatus); - if (error.data?.httpStatus === 409) { - setError("email", { - type: "manual", - message: "Cet email existe déjà", - }); - console.log(errors); - } - }, - }); - - const handleRegister: SubmitHandler = async (values) => { - await registerUser(values); - }; - - return ( - - Inscription - - - Email - - {errors.email?.message} - - - Prénom - - {errors.firstName?.message} - - - Nom - - {errors.lastName?.message} - - - Mot de passe - - {errors.password?.message} - - - - Confirmation mot de passe - - - value === getValues("password") || - "Les mots de passe ne correspondent pas", - })} - /> - - {errors.passwordConfirmation?.message} - - - - - - - - ); -} diff --git a/webapp/src/payload/collections/Categorie.ts b/webapp/src/payload/collections/Categorie.ts index a58625b9..0713e64c 100644 --- a/webapp/src/payload/collections/Categorie.ts +++ b/webapp/src/payload/collections/Categorie.ts @@ -6,6 +6,9 @@ export const Categories: CollectionConfig = { singular: "Catégorie", plural: "Catégories", }, + admin: { + useAsTitle: "label", + }, fields: [ { name: "slug", @@ -27,11 +30,5 @@ export const Categories: CollectionConfig = { required: true, relationTo: "media", }, - { - name: "color", - type: "text", - label: "Couleur", - required: true, - }, ], }; diff --git a/webapp/src/payload/collections/Coupon.ts b/webapp/src/payload/collections/Coupon.ts new file mode 100644 index 00000000..70b09ab7 --- /dev/null +++ b/webapp/src/payload/collections/Coupon.ts @@ -0,0 +1,57 @@ +import dynamic from "next/dynamic"; +import type { Props } from "payload/components/views/List"; +import { type CollectionConfig } from "payload/types"; + +const ImportCoupons = dynamic( + () => import("../components/ImportCoupons"), + { + ssr: false, + } +); + +export const Coupons: CollectionConfig = { + slug: "coupons", + labels: { + singular: "Bon de réduction", + plural: "Bons de réduction", + }, + fields: [ + { + name: "code", + type: "text", + label: "Code", + required: true + }, + { + name: "status", + type: "select", + label: "Statut", + options: [ + { label: "Disponible", value: "available" }, + { label: "Archivé", value: "archived" }, + ], + defaultValue: "available", + required: true, + }, + { + name: "user", + type: "relationship", + label: "Utilisateur", + relationTo: "users", + hasMany: false, + }, + { + name: "offer", + type: "relationship", + label: "Offre", + relationTo: "offers", + hasMany: false, + required: true, + }, + ], + admin: { + components: { + BeforeListTable: [ImportCoupons], + }, + }, +}; diff --git a/webapp/src/payload/collections/Offer.ts b/webapp/src/payload/collections/Offer.ts index 8d512f9c..b5cbf22a 100644 --- a/webapp/src/payload/collections/Offer.ts +++ b/webapp/src/payload/collections/Offer.ts @@ -6,6 +6,9 @@ export const Offers: CollectionConfig = { singular: "Offre", plural: "Offres", }, + admin: { + useAsTitle: "title", + }, fields: [ { name: "title", @@ -29,6 +32,12 @@ export const Offers: CollectionConfig = { hasMany: false, required: true, }, + { + name: "validityTo", + type: "date", + label: "Validité jusqu'au", + required: true, + }, { type: "select", name: "kind", diff --git a/webapp/src/payload/collections/Partner.ts b/webapp/src/payload/collections/Partner.ts index aacc6d40..a88f6bfb 100644 --- a/webapp/src/payload/collections/Partner.ts +++ b/webapp/src/payload/collections/Partner.ts @@ -6,6 +6,9 @@ export const Partners: CollectionConfig = { singular: "Partenaire", plural: "Partenaires", }, + admin: { + useAsTitle: "name", + }, fields: [ { name: "name", @@ -20,6 +23,18 @@ export const Partners: CollectionConfig = { label: "Description", required: true, }, + { + name: "url", + type: "text", + label: "URL", + required: true, + }, + { + name: "color", + type: "text", + label: "Couleur", + required: true, + }, { name: "icon", type: "upload", diff --git a/webapp/src/payload/collections/User.ts b/webapp/src/payload/collections/User.ts index 76f79e48..8365cd88 100644 --- a/webapp/src/payload/collections/User.ts +++ b/webapp/src/payload/collections/User.ts @@ -7,6 +7,9 @@ export const Users: CollectionConfig = { singular: "Utilisateur", plural: "Utilisateurs", }, + admin: { + useAsTitle: "email", + }, fields: [ { name: "email", diff --git a/webapp/src/payload/components/ImportCoupons.tsx b/webapp/src/payload/components/ImportCoupons.tsx new file mode 100644 index 00000000..a65f8a9e --- /dev/null +++ b/webapp/src/payload/components/ImportCoupons.tsx @@ -0,0 +1,184 @@ +import { QuestionOutlineIcon } from "@chakra-ui/icons"; +import { + Box, + Button, + Flex, + FormControl, + Heading, + Select, + Tooltip, +} from "@chakra-ui/react"; +import { Modal, useModal } from "@faceless-ui/modal"; +import { useMutation } from "@tanstack/react-query"; +import Papa from "papaparse"; +import { MinimalTemplate } from "payload/components/templates"; +import type { Props } from "payload/components/views/List"; +import usePayloadAPI from "payload/dist/admin/hooks/usePayloadAPI"; +import React, { useState } from "react"; +import { toast } from "react-toastify"; +import { OfferIncluded } from "~/server/api/routers/offer"; +import { api } from "~/utils/api"; +import { Coupon } from "../payload-types"; + +export type ImportCouponsProps = {}; + +const ImportCoupons = ({ hasCreatePermission, resetParams }: Props) => { + const { toggleModal } = useModal(); + const modalSlug = "modal-import-coupons"; + + const [csvFileValue, setCsvFileValue] = useState(""); + const [offerId, setOfferId] = useState(); + const [coupons, setCoupons] = useState([]); + + const [ + { + data: { docs }, + }, + ] = usePayloadAPI(`/api/offers`, { + initialParams: { + page: 1, + limit: 1000, + }, + }); + const offers = docs as OfferIncluded[]; + + const createCoupons = useMutation({ + mutationFn: (payload: Coupon) => { + return fetch("/api/coupons", { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(payload), + }); + }, + }); + + const handleFileChange = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + + if (file) { + setCsvFileValue(undefined); + Papa.parse(file, { + complete: (result) => { + const couponsToCreate = result.data + .filter((row: any) => row.code) + .map((row: any) => { + return { + code: row.code as string, + status: "available", + offer: -1, + } as Coupon; + }); + + if (!couponsToCreate.length) { + toast.error("Erreur dans le format du CSV"); + return; + } + + setCoupons(couponsToCreate); + toggleModal(modalSlug); + }, + header: true, + dynamicTyping: true, + }); + } + }; + + const validate = () => { + if (!offerId) return; + + if (coupons.length) { + const promises: Promise[] = []; + coupons.forEach((coupon) => { + promises.push(createCoupons.mutateAsync({ ...coupon, offer: offerId })); + }); + Promise.all(promises).then((responses) => { + const okRequestsCount = responses.filter((r) => r.ok).length; + const koRequestsCount = responses.filter((r) => !r.ok).length; + + if (okRequestsCount) + toast.success(`${okRequestsCount} bons de réduction importés`); + + if (koRequestsCount) + toast.error(`Erreur à l'import bons de réduction en base de donnée (${koRequestsCount} bons de réduction)`); + + setCoupons([]); + resetParams(); + }); + } + + toggleModal(modalSlug); + }; + + if (!hasCreatePermission || !offers) return; + + return ( + + { + setCsvFileValue(""); + }} + style={{ display: "none" }} + /> + + + + + + + + Offre pour laquelle importer des bons de réduction : + + + + + + + + + + + + ); +}; + +export default ImportCoupons; diff --git a/webapp/src/payload/migrations/20240125_091516.json b/webapp/src/payload/migrations/20240125_091516.json new file mode 100644 index 00000000..f0230cb9 --- /dev/null +++ b/webapp/src/payload/migrations/20240125_091516.json @@ -0,0 +1,1162 @@ +{ + "id": "eacd7eb3-0523-4147-aa2f-7ab1d2c342d1", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "5", + "dialect": "pg", + "tables": { + "admins": { + "name": "admins", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reset_password_token": { + "name": "reset_password_token", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reset_password_expiration": { + "name": "reset_password_expiration", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "salt": { + "name": "salt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hash": { + "name": "hash", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "login_attempts": { + "name": "login_attempts", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "email_idx": { + "name": "email_idx", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reset_password_token": { + "name": "reset_password_token", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reset_password_expiration": { + "name": "reset_password_expiration", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "salt": { + "name": "salt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hash": { + "name": "hash", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "login_attempts": { + "name": "login_attempts", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "email_idx": { + "name": "email_idx", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filesize": { + "name": "filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "filename_idx": { + "name": "filename_idx", + "columns": [ + "filename" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "slug_idx": { + "name": "slug_idx", + "columns": [ + "slug" + ], + "isUnique": true + }, + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "categories_rels": { + "name": "categories_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "categories_rels_parent_id_categories_id_fk": { + "name": "categories_rels_parent_id_categories_id_fk", + "tableFrom": "categories_rels", + "tableTo": "categories", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "categories_rels_media_id_media_id_fk": { + "name": "categories_rels_media_id_media_id_fk", + "tableFrom": "categories_rels", + "tableTo": "media", + "columnsFrom": [ + "media_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "partners": { + "name": "partners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "name_idx": { + "name": "name_idx", + "columns": [ + "name" + ], + "isUnique": true + }, + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "partners_rels": { + "name": "partners_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "partners_rels_parent_id_partners_id_fk": { + "name": "partners_rels_parent_id_partners_id_fk", + "tableFrom": "partners_rels", + "tableTo": "partners", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "partners_rels_media_id_media_id_fk": { + "name": "partners_rels_media_id_media_id_fk", + "tableFrom": "partners_rels", + "tableTo": "media", + "columnsFrom": [ + "media_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "offers": { + "name": "offers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "validity_to": { + "name": "validity_to", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "enum_offers_kind", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "offers_rels": { + "name": "offers_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "partners_id": { + "name": "partners_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "categories_id": { + "name": "categories_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "offers_rels_parent_id_offers_id_fk": { + "name": "offers_rels_parent_id_offers_id_fk", + "tableFrom": "offers_rels", + "tableTo": "offers", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "offers_rels_partners_id_partners_id_fk": { + "name": "offers_rels_partners_id_partners_id_fk", + "tableFrom": "offers_rels", + "tableTo": "partners", + "columnsFrom": [ + "partners_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "offers_rels_categories_id_categories_id_fk": { + "name": "offers_rels_categories_id_categories_id_fk", + "tableFrom": "offers_rels", + "tableTo": "categories", + "columnsFrom": [ + "categories_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "coupons": { + "name": "coupons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_coupons_status", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "code_idx": { + "name": "code_idx", + "columns": [ + "code" + ], + "isUnique": true + }, + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "coupons_rels": { + "name": "coupons_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "offers_id": { + "name": "offers_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "coupons_rels_parent_id_coupons_id_fk": { + "name": "coupons_rels_parent_id_coupons_id_fk", + "tableFrom": "coupons_rels", + "tableTo": "coupons", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "coupons_rels_users_id_users_id_fk": { + "name": "coupons_rels_users_id_users_id_fk", + "tableFrom": "coupons_rels", + "tableTo": "users", + "columnsFrom": [ + "users_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "coupons_rels_offers_id_offers_id_fk": { + "name": "coupons_rels_offers_id_offers_id_fk", + "tableFrom": "coupons_rels", + "tableTo": "offers", + "columnsFrom": [ + "offers_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "payload_preferences": { + "name": "payload_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "payload_preferences_rels": { + "name": "payload_preferences_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "admins_id": { + "name": "admins_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "payload_preferences_rels_parent_id_payload_preferences_id_fk": { + "name": "payload_preferences_rels_parent_id_payload_preferences_id_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "payload_preferences", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_admins_id_admins_id_fk": { + "name": "payload_preferences_rels_admins_id_admins_id_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "admins", + "columnsFrom": [ + "admins_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_users_id_users_id_fk": { + "name": "payload_preferences_rels_users_id_users_id_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "users", + "columnsFrom": [ + "users_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "payload_migrations": { + "name": "payload_migrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "batch": { + "name": "batch", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "_locales": { + "name": "_locales", + "values": { + "fr": "fr" + } + }, + "enum_offers_kind": { + "name": "enum_offers_kind", + "values": { + "voucher": "voucher", + "code": "code" + } + }, + "enum_coupons_status": { + "name": "enum_coupons_status", + "values": { + "available": "available", + "archived": "archived" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/webapp/src/payload/migrations/20240125_091516.ts b/webapp/src/payload/migrations/20240125_091516.ts new file mode 100644 index 00000000..92614682 --- /dev/null +++ b/webapp/src/payload/migrations/20240125_091516.ts @@ -0,0 +1,291 @@ +import { MigrateUpArgs, MigrateDownArgs } from '@payloadcms/db-postgres' +import { sql } from 'drizzle-orm' + +export async function up({ payload }: MigrateUpArgs): Promise { +await payload.db.drizzle.execute(sql` + +DO $$ BEGIN + CREATE TYPE "_locales" AS ENUM('fr'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "enum_offers_kind" AS ENUM('voucher', 'code'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "enum_coupons_status" AS ENUM('available', 'archived'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +CREATE TABLE IF NOT EXISTS "admins" ( + "id" serial PRIMARY KEY NOT NULL, + "first_name" varchar NOT NULL, + "last_name" varchar NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "email" varchar NOT NULL, + "reset_password_token" varchar, + "reset_password_expiration" timestamp(3) with time zone, + "salt" varchar, + "hash" varchar, + "login_attempts" numeric, + "lock_until" timestamp(3) with time zone +); + +CREATE TABLE IF NOT EXISTS "users" ( + "id" serial PRIMARY KEY NOT NULL, + "first_name" varchar NOT NULL, + "last_name" varchar NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "email" varchar NOT NULL, + "reset_password_token" varchar, + "reset_password_expiration" timestamp(3) with time zone, + "salt" varchar, + "hash" varchar, + "login_attempts" numeric, + "lock_until" timestamp(3) with time zone +); + +CREATE TABLE IF NOT EXISTS "media" ( + "id" serial PRIMARY KEY NOT NULL, + "alt" varchar, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "url" varchar, + "filename" varchar, + "mime_type" varchar, + "filesize" numeric, + "width" numeric, + "height" numeric +); + +CREATE TABLE IF NOT EXISTS "categories" ( + "id" serial PRIMARY KEY NOT NULL, + "slug" varchar NOT NULL, + "label" varchar NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE IF NOT EXISTS "categories_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "media_id" integer +); + +CREATE TABLE IF NOT EXISTS "partners" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar NOT NULL, + "description" varchar NOT NULL, + "url" varchar NOT NULL, + "color" varchar NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE IF NOT EXISTS "partners_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "media_id" integer +); + +CREATE TABLE IF NOT EXISTS "offers" ( + "id" serial PRIMARY KEY NOT NULL, + "title" varchar NOT NULL, + "validity_to" timestamp(3) with time zone NOT NULL, + "kind" "enum_offers_kind" NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE IF NOT EXISTS "offers_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "partners_id" integer, + "categories_id" integer +); + +CREATE TABLE IF NOT EXISTS "coupons" ( + "id" serial PRIMARY KEY NOT NULL, + "code" varchar NOT NULL, + "status" "enum_coupons_status" NOT NULL, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE IF NOT EXISTS "coupons_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "users_id" integer, + "offers_id" integer +); + +CREATE TABLE IF NOT EXISTS "payload_preferences" ( + "id" serial PRIMARY KEY NOT NULL, + "key" varchar, + "value" jsonb, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL +); + +CREATE TABLE IF NOT EXISTS "payload_preferences_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" integer NOT NULL, + "path" varchar NOT NULL, + "admins_id" integer, + "users_id" integer +); + +CREATE TABLE IF NOT EXISTS "payload_migrations" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar, + "batch" numeric, + "updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL, + "created_at" timestamp(3) with time zone DEFAULT now() NOT NULL +); + +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "admins" ("created_at"); +CREATE UNIQUE INDEX IF NOT EXISTS "email_idx" ON "admins" ("email"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "users" ("created_at"); +CREATE UNIQUE INDEX IF NOT EXISTS "email_idx" ON "users" ("email"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "media" ("created_at"); +CREATE UNIQUE INDEX IF NOT EXISTS "filename_idx" ON "media" ("filename"); +CREATE UNIQUE INDEX IF NOT EXISTS "slug_idx" ON "categories" ("slug"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "categories" ("created_at"); +CREATE INDEX IF NOT EXISTS "order_idx" ON "categories_rels" ("order"); +CREATE INDEX IF NOT EXISTS "parent_idx" ON "categories_rels" ("parent_id"); +CREATE INDEX IF NOT EXISTS "path_idx" ON "categories_rels" ("path"); +CREATE UNIQUE INDEX IF NOT EXISTS "name_idx" ON "partners" ("name"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "partners" ("created_at"); +CREATE INDEX IF NOT EXISTS "order_idx" ON "partners_rels" ("order"); +CREATE INDEX IF NOT EXISTS "parent_idx" ON "partners_rels" ("parent_id"); +CREATE INDEX IF NOT EXISTS "path_idx" ON "partners_rels" ("path"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "offers" ("created_at"); +CREATE INDEX IF NOT EXISTS "order_idx" ON "offers_rels" ("order"); +CREATE INDEX IF NOT EXISTS "parent_idx" ON "offers_rels" ("parent_id"); +CREATE INDEX IF NOT EXISTS "path_idx" ON "offers_rels" ("path"); +CREATE UNIQUE INDEX IF NOT EXISTS "code_idx" ON "coupons" ("code"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "coupons" ("created_at"); +CREATE INDEX IF NOT EXISTS "order_idx" ON "coupons_rels" ("order"); +CREATE INDEX IF NOT EXISTS "parent_idx" ON "coupons_rels" ("parent_id"); +CREATE INDEX IF NOT EXISTS "path_idx" ON "coupons_rels" ("path"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "payload_preferences" ("created_at"); +CREATE INDEX IF NOT EXISTS "order_idx" ON "payload_preferences_rels" ("order"); +CREATE INDEX IF NOT EXISTS "parent_idx" ON "payload_preferences_rels" ("parent_id"); +CREATE INDEX IF NOT EXISTS "path_idx" ON "payload_preferences_rels" ("path"); +CREATE INDEX IF NOT EXISTS "created_at_idx" ON "payload_migrations" ("created_at"); +DO $$ BEGIN + ALTER TABLE "categories_rels" ADD CONSTRAINT "categories_rels_parent_id_categories_id_fk" FOREIGN KEY ("parent_id") REFERENCES "categories"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "categories_rels" ADD CONSTRAINT "categories_rels_media_id_media_id_fk" FOREIGN KEY ("media_id") REFERENCES "media"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "partners_rels" ADD CONSTRAINT "partners_rels_parent_id_partners_id_fk" FOREIGN KEY ("parent_id") REFERENCES "partners"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "partners_rels" ADD CONSTRAINT "partners_rels_media_id_media_id_fk" FOREIGN KEY ("media_id") REFERENCES "media"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "offers_rels" ADD CONSTRAINT "offers_rels_parent_id_offers_id_fk" FOREIGN KEY ("parent_id") REFERENCES "offers"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "offers_rels" ADD CONSTRAINT "offers_rels_partners_id_partners_id_fk" FOREIGN KEY ("partners_id") REFERENCES "partners"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "offers_rels" ADD CONSTRAINT "offers_rels_categories_id_categories_id_fk" FOREIGN KEY ("categories_id") REFERENCES "categories"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "coupons_rels" ADD CONSTRAINT "coupons_rels_parent_id_coupons_id_fk" FOREIGN KEY ("parent_id") REFERENCES "coupons"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "coupons_rels" ADD CONSTRAINT "coupons_rels_users_id_users_id_fk" FOREIGN KEY ("users_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "coupons_rels" ADD CONSTRAINT "coupons_rels_offers_id_offers_id_fk" FOREIGN KEY ("offers_id") REFERENCES "offers"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_parent_id_payload_preferences_id_fk" FOREIGN KEY ("parent_id") REFERENCES "payload_preferences"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_admins_id_admins_id_fk" FOREIGN KEY ("admins_id") REFERENCES "admins"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "payload_preferences_rels" ADD CONSTRAINT "payload_preferences_rels_users_id_users_id_fk" FOREIGN KEY ("users_id") REFERENCES "users"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; +`); + +}; + +export async function down({ payload }: MigrateDownArgs): Promise { +await payload.db.drizzle.execute(sql` + +DROP TABLE "admins"; +DROP TABLE "users"; +DROP TABLE "media"; +DROP TABLE "categories"; +DROP TABLE "categories_rels"; +DROP TABLE "partners"; +DROP TABLE "partners_rels"; +DROP TABLE "offers"; +DROP TABLE "offers_rels"; +DROP TABLE "coupons"; +DROP TABLE "coupons_rels"; +DROP TABLE "payload_preferences"; +DROP TABLE "payload_preferences_rels"; +DROP TABLE "payload_migrations";`); + +}; diff --git a/webapp/src/payload/migrations/20240126_145518.json b/webapp/src/payload/migrations/20240126_145518.json new file mode 100644 index 00000000..97a93a05 --- /dev/null +++ b/webapp/src/payload/migrations/20240126_145518.json @@ -0,0 +1,1155 @@ +{ + "id": "170fa0ed-2d42-47a4-8b4e-357995933d5c", + "prevId": "00000000-0000-0000-0000-000000000000", + "version": "5", + "dialect": "pg", + "tables": { + "admins": { + "name": "admins", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reset_password_token": { + "name": "reset_password_token", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reset_password_expiration": { + "name": "reset_password_expiration", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "salt": { + "name": "salt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hash": { + "name": "hash", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "login_attempts": { + "name": "login_attempts", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "email_idx": { + "name": "email_idx", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users": { + "name": "users", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "first_name": { + "name": "first_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "last_name": { + "name": "last_name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "email": { + "name": "email", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "reset_password_token": { + "name": "reset_password_token", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "reset_password_expiration": { + "name": "reset_password_expiration", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + }, + "salt": { + "name": "salt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "hash": { + "name": "hash", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "login_attempts": { + "name": "login_attempts", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "lock_until": { + "name": "lock_until", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "email_idx": { + "name": "email_idx", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "media": { + "name": "media", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "alt": { + "name": "alt", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filename": { + "name": "filename", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "mime_type": { + "name": "mime_type", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "filesize": { + "name": "filesize", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "width": { + "name": "width", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "height": { + "name": "height", + "type": "numeric", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "filename_idx": { + "name": "filename_idx", + "columns": [ + "filename" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "categories": { + "name": "categories", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "label": { + "name": "label", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "slug_idx": { + "name": "slug_idx", + "columns": [ + "slug" + ], + "isUnique": true + }, + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "categories_rels": { + "name": "categories_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "categories_rels_parent_id_categories_id_fk": { + "name": "categories_rels_parent_id_categories_id_fk", + "tableFrom": "categories_rels", + "tableTo": "categories", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "categories_rels_media_id_media_id_fk": { + "name": "categories_rels_media_id_media_id_fk", + "tableFrom": "categories_rels", + "tableTo": "media", + "columnsFrom": [ + "media_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "partners": { + "name": "partners", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "url": { + "name": "url", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "color": { + "name": "color", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "name_idx": { + "name": "name_idx", + "columns": [ + "name" + ], + "isUnique": true + }, + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "partners_rels": { + "name": "partners_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "media_id": { + "name": "media_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "partners_rels_parent_id_partners_id_fk": { + "name": "partners_rels_parent_id_partners_id_fk", + "tableFrom": "partners_rels", + "tableTo": "partners", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "partners_rels_media_id_media_id_fk": { + "name": "partners_rels_media_id_media_id_fk", + "tableFrom": "partners_rels", + "tableTo": "media", + "columnsFrom": [ + "media_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "offers": { + "name": "offers", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "validity_to": { + "name": "validity_to", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "enum_offers_kind", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "offers_rels": { + "name": "offers_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "partners_id": { + "name": "partners_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "categories_id": { + "name": "categories_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "offers_rels_parent_id_offers_id_fk": { + "name": "offers_rels_parent_id_offers_id_fk", + "tableFrom": "offers_rels", + "tableTo": "offers", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "offers_rels_partners_id_partners_id_fk": { + "name": "offers_rels_partners_id_partners_id_fk", + "tableFrom": "offers_rels", + "tableTo": "partners", + "columnsFrom": [ + "partners_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "offers_rels_categories_id_categories_id_fk": { + "name": "offers_rels_categories_id_categories_id_fk", + "tableFrom": "offers_rels", + "tableTo": "categories", + "columnsFrom": [ + "categories_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "coupons": { + "name": "coupons", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "code": { + "name": "code", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "enum_coupons_status", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "coupons_rels": { + "name": "coupons_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "offers_id": { + "name": "offers_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "coupons_rels_parent_id_coupons_id_fk": { + "name": "coupons_rels_parent_id_coupons_id_fk", + "tableFrom": "coupons_rels", + "tableTo": "coupons", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "coupons_rels_users_id_users_id_fk": { + "name": "coupons_rels_users_id_users_id_fk", + "tableFrom": "coupons_rels", + "tableTo": "users", + "columnsFrom": [ + "users_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "coupons_rels_offers_id_offers_id_fk": { + "name": "coupons_rels_offers_id_offers_id_fk", + "tableFrom": "coupons_rels", + "tableTo": "offers", + "columnsFrom": [ + "offers_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "payload_preferences": { + "name": "payload_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "key": { + "name": "key", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "payload_preferences_rels": { + "name": "payload_preferences_rels", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "parent_id": { + "name": "parent_id", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "path": { + "name": "path", + "type": "varchar", + "primaryKey": false, + "notNull": true + }, + "admins_id": { + "name": "admins_id", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "users_id": { + "name": "users_id", + "type": "integer", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "order_idx": { + "name": "order_idx", + "columns": [ + "order" + ], + "isUnique": false + }, + "parent_idx": { + "name": "parent_idx", + "columns": [ + "parent_id" + ], + "isUnique": false + }, + "path_idx": { + "name": "path_idx", + "columns": [ + "path" + ], + "isUnique": false + } + }, + "foreignKeys": { + "payload_preferences_rels_parent_id_payload_preferences_id_fk": { + "name": "payload_preferences_rels_parent_id_payload_preferences_id_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "payload_preferences", + "columnsFrom": [ + "parent_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_admins_id_admins_id_fk": { + "name": "payload_preferences_rels_admins_id_admins_id_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "admins", + "columnsFrom": [ + "admins_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "payload_preferences_rels_users_id_users_id_fk": { + "name": "payload_preferences_rels_users_id_users_id_fk", + "tableFrom": "payload_preferences_rels", + "tableTo": "users", + "columnsFrom": [ + "users_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "payload_migrations": { + "name": "payload_migrations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "serial", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar", + "primaryKey": false, + "notNull": false + }, + "batch": { + "name": "batch", + "type": "numeric", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "created_at": { + "name": "created_at", + "type": "timestamp(3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "created_at_idx": { + "name": "created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + } + }, + "enums": { + "_locales": { + "name": "_locales", + "values": { + "fr": "fr" + } + }, + "enum_offers_kind": { + "name": "enum_offers_kind", + "values": { + "voucher": "voucher", + "code": "code" + } + }, + "enum_coupons_status": { + "name": "enum_coupons_status", + "values": { + "available": "available", + "archived": "archived" + } + } + }, + "schemas": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/webapp/src/payload/migrations/20240126_145518.ts b/webapp/src/payload/migrations/20240126_145518.ts new file mode 100644 index 00000000..c427f547 --- /dev/null +++ b/webapp/src/payload/migrations/20240126_145518.ts @@ -0,0 +1,16 @@ +import { MigrateUpArgs, MigrateDownArgs } from '@payloadcms/db-postgres' +import { sql } from 'drizzle-orm' + +export async function up({ payload }: MigrateUpArgs): Promise { +await payload.db.drizzle.execute(sql` + +DROP INDEX IF EXISTS "code_idx";`); + +}; + +export async function down({ payload }: MigrateDownArgs): Promise { +await payload.db.drizzle.execute(sql` + +CREATE UNIQUE INDEX IF NOT EXISTS "code_idx" ON "coupons" ("code");`); + +}; diff --git a/webapp/src/payload/payload-types.ts b/webapp/src/payload/payload-types.ts index 6fe28f2a..91a7089f 100644 --- a/webapp/src/payload/payload-types.ts +++ b/webapp/src/payload/payload-types.ts @@ -14,6 +14,7 @@ export interface Config { categories: Category; partners: Partner; offers: Offer; + coupons: Coupon; 'payload-preferences': PayloadPreference; 'payload-migrations': PayloadMigration; }; @@ -60,39 +61,12 @@ export interface Media { filesize?: number | null; width?: number | null; height?: number | null; - sizes?: { - thumbnail?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - card?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - tablet?: { - url?: string | null; - width?: number | null; - height?: number | null; - mimeType?: string | null; - filesize?: number | null; - filename?: string | null; - }; - }; } export interface Category { id: number; slug: string; label: string; icon: number | Media; - color: string; updatedAt: string; createdAt: string; } @@ -100,6 +74,8 @@ export interface Partner { id: number; name: string; description: string; + url: string; + color: string; icon: number | Media; updatedAt: string; createdAt: string; @@ -109,10 +85,20 @@ export interface Offer { title: string; partner: number | Partner; category: number | Category; + validityTo: string; kind: 'voucher' | 'code'; updatedAt: string; createdAt: string; } +export interface Coupon { + id: number; + code: string; + status: 'available' | 'archived'; + user?: (number | null) | User; + offer: number | Offer; + updatedAt: string; + createdAt: string; +} export interface PayloadPreference { id: number; user: diff --git a/webapp/src/payload/payload.config.ts b/webapp/src/payload/payload.config.ts index 4fac6afc..6b0bd5c0 100644 --- a/webapp/src/payload/payload.config.ts +++ b/webapp/src/payload/payload.config.ts @@ -12,6 +12,7 @@ import { Media } from "./collections/Media"; import { Categories } from "./collections/Categorie"; import { Partners } from "./collections/Partner"; import { Offers } from "./collections/Offer"; +import { Coupons } from "./collections/Coupon"; const adapter = s3Adapter({ config: { @@ -27,11 +28,12 @@ const adapter = s3Adapter({ export default buildConfig({ db: postgresAdapter({ + migrationDir: path.resolve(__dirname, "./migrations"), pool: { connectionString: process.env.NODE_ENV !== "production" ? process.env.DATABASE_URL - : `postgresql://${process.env.PGDATABASE}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}?sslmode=disable`, + : `postgresql://${process.env.PGUSER}:${process.env.PGPASSWORD}@${process.env.PGHOST}:${process.env.PGPORT}/${process.env.PGDATABASE}?sslmode=disable`, }, }), plugins: [ @@ -51,7 +53,7 @@ export default buildConfig({ bundler: webpackBundler(), user: "admins", }, - collections: [Admins, Users, Media, Categories, Partners, Offers], + collections: [Admins, Users, Media, Categories, Partners, Offers, Coupons], localization: { locales: ["fr"], defaultLocale: "fr", diff --git a/webapp/src/payload/payloadClient.ts b/webapp/src/payload/payloadClient.ts index f80a7f11..31b9afee 100644 --- a/webapp/src/payload/payloadClient.ts +++ b/webapp/src/payload/payloadClient.ts @@ -9,40 +9,40 @@ import config from "./payload.config"; * Source: https://github.com/vercel/next.js/blob/canary/examples/with-mongodb-mongoose/lib/dbConnect.js */ let cached: { - client: Payload | null; - promise: Promise | null; + client: Payload | null; + promise: Promise | null; } = (global as any).payload; if (!cached) { - cached = (global as any).payload = { client: null, promise: null }; + cached = (global as any).payload = { client: null, promise: null }; } interface Args { - seed?: boolean; + seed?: boolean; } export const getPayloadClient = async (args: Args): Promise => { - if (cached.client) { - return cached.client; - } - - if (!cached.promise) { - cached.promise = getPayload({ - // Make sure that your environment variables are filled out accordingly - secret: process.env.PAYLOAD_SECRET as string, - config: config, - local: args?.seed ?? false, - }); - } - - try { - cached.client = await cached.promise; - } catch (e) { - cached.promise = null; - throw e; - } - - return cached.client; + if (cached.client) { + return cached.client; + } + + if (!cached.promise) { + cached.promise = getPayload({ + // Make sure that your environment variables are filled out accordingly + secret: process.env.PAYLOAD_SECRET as string, + config: config, + local: args?.seed ?? false, + }); + } + + try { + cached.client = await cached.promise; + } catch (e) { + cached.promise = null; + throw e; + } + + return cached.client; }; export default getPayloadClient; diff --git a/webapp/src/payload/seed/categories.ts b/webapp/src/payload/seed/categories.ts index 830ac4e0..97293a4d 100644 --- a/webapp/src/payload/seed/categories.ts +++ b/webapp/src/payload/seed/categories.ts @@ -5,27 +5,38 @@ export const categories = [ { slug: "shop", label: "Courses", - color: "#FACBD1", }, { - slug: "care", - label: "Soins", - color: "#CDE0FF", + slug: "mobility", + label: "Mobilité", }, { - slug: "services", - label: "Services", - color: "#DBF0EB", + slug: "hobby", + label: "Loisirs", }, { - slug: "telephony", - label: "Téléphonie", - color: "#FEDF6B", + slug: "sport", + label: "Sport", }, { - slug: "mobility", - label: "Mobilité", - color: "#CACAFB", + slug: "hygiene", + label: "Hygiène", + }, + { + slug: "equipment", + label: "Équipement", + }, + { + slug: "hosting", + label: "Hébergement", + }, + { + slug: "bank", + label: "Banque, mutuelle, assurance", + }, + { + slug: "telephony", + label: "Téléphone, internet", }, ] as const; @@ -35,9 +46,7 @@ export async function seedCategories(payload: Payload) { collection: "media", filePath: path.join( __dirname, - `../../../public/images/seeds/categories/${category.slug}.${ - category.slug === "mobility" ? "png" : "svg" - }` + `../../../public/images/seeds/categories/${category.slug}.png` ), data: { alt: `${category.slug} icon`, diff --git a/webapp/src/payload/seed/offers.ts b/webapp/src/payload/seed/offers.ts index e5ee38c4..b3bd06f5 100644 --- a/webapp/src/payload/seed/offers.ts +++ b/webapp/src/payload/seed/offers.ts @@ -4,13 +4,19 @@ import { partners } from "./partners"; import { categories } from "./categories"; export async function seedOffers(payload: Payload) { + const currentDate = new Date(); + const validityTo = new Date( + currentDate.setMonth(currentDate.getMonth() + 1) + ).toISOString(); + const offers: Omit[] = [ { title: "15% de réduction sur les produits alimentaire", - partner: partners.findIndex((partner) => partner.name === "Leclerc") + 1, + partner: partners.findIndex((partner) => partner.name === "Cora") + 1, category: categories.findIndex((category) => category.slug === "shop") + 1, kind: "voucher", + validityTo, }, { title: "10% de réduction sur plus de 50 produits alimentaire", @@ -18,6 +24,7 @@ export async function seedOffers(payload: Payload) { category: categories.findIndex((category) => category.slug === "shop") + 1, kind: "voucher", + validityTo, }, { title: "10% de réduction les produits alimentaire", @@ -25,6 +32,7 @@ export async function seedOffers(payload: Payload) { category: categories.findIndex((category) => category.slug === "shop") + 1, kind: "voucher", + validityTo, }, { title: "10% de réduction sur l’ensemble des billets en France et Europe", @@ -32,6 +40,7 @@ export async function seedOffers(payload: Payload) { category: categories.findIndex((category) => category.slug === "mobility") + 1, kind: "code", + validityTo, }, ]; diff --git a/webapp/src/payload/seed/partners.ts b/webapp/src/payload/seed/partners.ts index 879605f0..24f012ba 100644 --- a/webapp/src/payload/seed/partners.ts +++ b/webapp/src/payload/seed/partners.ts @@ -3,20 +3,28 @@ import { type Payload } from "payload"; export const partners = [ { - name: "Leclerc", - description: "Description de Leclerc", + name: "Cora", + description: "Description de Cora", + color: "#283F93", + url: "https://www.cora.fr/", }, { name: "Lidl", description: "Description de Lidl", + color: "#FFF000", + url: "https://www.lidl.fr/", }, { name: "Auchan", description: "Description de Auchan", + color: "#E0001A", + url: "https://www.auchan.fr/", }, { name: "Flixbus", description: "Description de Flixbus", + color: "#73D700", + url: "https://www.flixbus.fr/", }, ] as const; diff --git a/webapp/src/providers/Auth.tsx b/webapp/src/providers/Auth.tsx index da1e49b6..ec7eddd7 100644 --- a/webapp/src/providers/Auth.tsx +++ b/webapp/src/providers/Auth.tsx @@ -18,7 +18,7 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ useEffect(() => { const fetchMe = async () => { - const token = getCookie(process.env.NEXT_PUBLIC_JWT_NAME as string); + const token = getCookie(process.env.NEXT_PUBLIC_JWT_NAME ?? "cje-jwt"); if (!token) return; const result = await fetch("/api/users/me", { headers: { @@ -29,8 +29,8 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ setUser(result.user); } else { setUser(null); - deleteCookie(process.env.NEXT_PUBLIC_JWT_NAME as string); - router.push("/login"); + deleteCookie(process.env.NEXT_PUBLIC_JWT_NAME ?? "cje-jwt"); + router.push("/"); } }; diff --git a/webapp/src/server/api/root.ts b/webapp/src/server/api/root.ts index 5b8e3b32..fca8bed7 100644 --- a/webapp/src/server/api/root.ts +++ b/webapp/src/server/api/root.ts @@ -3,6 +3,7 @@ import { createTRPCRouter } from "~/server/api/trpc"; import { userRouter } from "./routers/user"; import { categoryRouter } from "./routers/category"; import { offerRouter } from "./routers/offer"; +import { couponRouter } from "./routers/coupon"; /** * This is the primary router for your server. @@ -13,6 +14,7 @@ export const appRouter = createTRPCRouter({ user: userRouter, category: categoryRouter, offer: offerRouter, + coupon: couponRouter, }); // export type definition of API diff --git a/webapp/src/server/api/routers/category.ts b/webapp/src/server/api/routers/category.ts index bd120f7f..8666fc6b 100644 --- a/webapp/src/server/api/routers/category.ts +++ b/webapp/src/server/api/routers/category.ts @@ -1,8 +1,10 @@ -import type { Category, Media } from "~/payload/payload-types"; -import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc"; -import { ZGetListParams } from "~/server/types"; +import { TRPCError } from '@trpc/server'; +import { z } from 'zod'; +import type { Category, Media } from '~/payload/payload-types'; +import { createTRPCRouter, protectedProcedure } from '~/server/api/trpc'; +import { ZGetListParams } from '~/server/types'; -interface CategoryIncluded extends Category { +export interface CategoryIncluded extends Category { icon: Media; } @@ -13,15 +15,39 @@ export const categoryRouter = createTRPCRouter({ const { perPage, page, sort } = input; const categories = await ctx.payload.find({ - collection: "categories", + collection: 'categories', limit: perPage, page: page, - sort, + sort }); return { data: categories.docs as CategoryIncluded[], - metadata: { page, count: categories.docs.length }, + metadata: { page, count: categories.docs.length } }; }), + + getBySlug: protectedProcedure + .input(z.object({ slug: z.string() })) + .query(async ({ ctx, input }) => { + const { slug } = input; + + const category = await ctx.payload.find({ + collection: 'categories', + where: { + slug: { + equals: slug + } + } + }); + + if (!category.docs.length) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'Category not found' + }); + } + + return { data: category.docs[0] as CategoryIncluded }; + }) }); diff --git a/webapp/src/server/api/routers/coupon.ts b/webapp/src/server/api/routers/coupon.ts new file mode 100644 index 00000000..e883d278 --- /dev/null +++ b/webapp/src/server/api/routers/coupon.ts @@ -0,0 +1,68 @@ +import { TRPCError } from '@trpc/server'; +import { z } from 'zod'; +import { Coupon, Media, Offer, Partner, User } from '~/payload/payload-types'; +import { createTRPCRouter, protectedProcedure } from '~/server/api/trpc'; + +export interface CouponIncluded extends Coupon { + offer: Offer & { icon: Media; partner: Partner }; + userRouter: User; +} + +export const couponRouter = createTRPCRouter({ + getOne: protectedProcedure + .input(z.object({ offer_id: z.number() })) + .query(async ({ ctx, input }) => { + const { offer_id } = input; + + const coupons = await ctx.payload.find({ + collection: 'coupons', + depth: 2, + where: { + and: [ + { offer: { equals: offer_id } }, + { status: { equals: 'available' } }, + { user: { equals: ctx.session.id } } + ] + } + }); + + return { data: coupons.docs[0] as CouponIncluded }; + }), + + assignToUser: protectedProcedure + .input(z.object({ offer_id: z.number() })) + .mutation(async ({ ctx, input }) => { + const { offer_id } = input; + + const coupons = await ctx.payload.find({ + collection: 'coupons', + where: { + and: [ + { offer: { equals: offer_id } }, + { status: { equals: 'available' } } + ] + } + }); + + const couponsFiltered = coupons.docs.filter( + coupon => coupon.user === undefined || coupon.user === null + ); + + if (couponsFiltered.length === 0) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: 'No coupons available for this offer' + }); + } + + const couponData = couponsFiltered[0] as CouponIncluded; + + const updatedCoupon = await ctx.payload.update({ + collection: 'coupons', + id: couponData.id, + data: { user: ctx.session.id } + }); + + return { data: updatedCoupon }; + }) +}); diff --git a/webapp/src/server/api/routers/offer.ts b/webapp/src/server/api/routers/offer.ts index 602afb3f..0dac206c 100644 --- a/webapp/src/server/api/routers/offer.ts +++ b/webapp/src/server/api/routers/offer.ts @@ -1,28 +1,74 @@ +import { z } from "zod"; import { Category, Offer, Media, Partner } from "~/payload/payload-types"; import { createTRPCRouter, protectedProcedure } from "~/server/api/trpc"; import { ZGetListParams } from "~/server/types"; -interface OfferIncluded extends Offer { +export interface OfferIncluded extends Offer { partner: Partner & { icon: Media }; category: Category & { icon: Media }; } export const offerRouter = createTRPCRouter({ getList: protectedProcedure - .input(ZGetListParams) + .input( + ZGetListParams.merge(z.object({ categoryId: z.number().optional() })) + ) .query(async ({ ctx, input }) => { - const { perPage, page, sort } = input; + const { perPage, page, sort, categoryId } = input; const offers = await ctx.payload.find({ collection: "offers", limit: perPage, page: page, + where: categoryId + ? { + category: { + equals: categoryId, + }, + } + : {}, sort, }); + const couponCountOfOffers = await ctx.payload.find({ + collection: "coupons", + depth: 0, + where: { + offer: { + in: offers.docs.map((offer) => offer.id), + }, + }, + }); + + const offersFiltered = offers.docs.filter((offer) => { + const couponCount = couponCountOfOffers.docs.filter( + (coupon) => + coupon.offer === offer.id && + coupon.status === "available" && + (coupon.user === undefined || + coupon.user === null || + coupon.user === ctx.session.id) + ).length; + + if (couponCount > 0) return offer; + }); + return { - data: offers.docs as OfferIncluded[], + data: offersFiltered as OfferIncluded[], metadata: { page, count: offers.docs.length }, }; }), + + getById: protectedProcedure + .input(z.object({ id: z.number() })) + .query(async ({ ctx, input }) => { + const { id } = input; + + const offer = await ctx.payload.findByID({ + collection: "offers", + id, + }); + + return { data: offer as OfferIncluded }; + }), }); diff --git a/webapp/src/server/api/routers/user.ts b/webapp/src/server/api/routers/user.ts index f55da709..79d5be15 100644 --- a/webapp/src/server/api/routers/user.ts +++ b/webapp/src/server/api/routers/user.ts @@ -1,11 +1,11 @@ -import { TRPCError } from "@trpc/server"; -import APIError from "payload/dist/errors/APIError"; -import { z } from "zod"; +import { TRPCError } from '@trpc/server'; +import APIError from 'payload/dist/errors/APIError'; +import { z } from 'zod'; import { createTRPCRouter, protectedProcedure, - publicProcedure, -} from "~/server/api/trpc"; + publicProcedure +} from '~/server/api/trpc'; export const userRouter = createTRPCRouter({ register: publicProcedure @@ -14,34 +14,34 @@ export const userRouter = createTRPCRouter({ email: z.string().email(), firstName: z.string(), lastName: z.string(), - password: z.string(), + password: z.string() }) ) .mutation(async ({ ctx, input: userInput }) => { try { const newUser = await ctx.payload.create({ - collection: "users", - data: userInput, + collection: 'users', + data: userInput }); return { data: newUser }; } catch (error: unknown) { if (error instanceof APIError) { if ( - error.data[0].field === "email" && - error.data[0].message.includes("registered") + error.data[0].field === 'email' && + error.data[0].message.includes('registered') ) { throw new TRPCError({ - code: "CONFLICT", - message: "Email already registered", - cause: error, + code: 'CONFLICT', + message: 'Email already registered', + cause: error }); } } throw new TRPCError({ - code: "BAD_REQUEST", - message: "Unknown error", - cause: error, + code: 'BAD_REQUEST', + message: 'Unknown error', + cause: error }); } }), @@ -50,39 +50,33 @@ export const userRouter = createTRPCRouter({ .input( z.object({ email: z.string().email(), - password: z.string(), + password: z.string() }) ) .mutation(async ({ ctx, input: userInput }) => { try { const user = await ctx.payload.login({ - collection: "users", - data: userInput, + collection: 'users', + data: userInput }); return { data: user }; } catch (error) { - if (error && typeof error === "object" && "status" in error) { + if (error && typeof error === 'object' && 'status' in error) { if (error.status === 401) { throw new TRPCError({ - code: "UNAUTHORIZED", - message: "Invalid email or password", - cause: error, + code: 'UNAUTHORIZED', + message: 'Invalid email or password', + cause: error }); } } throw new TRPCError({ - code: "BAD_REQUEST", - message: "Unknown error", - cause: error, + code: 'BAD_REQUEST', + message: 'Unknown error', + cause: error }); } - }), - - logout: protectedProcedure.mutation(async () => { - await fetch("/api/users/logout"); - - return { data: true }; - }), + }) }); diff --git a/webapp/src/server/api/trpc.ts b/webapp/src/server/api/trpc.ts index ce03fab1..a07f7e04 100644 --- a/webapp/src/server/api/trpc.ts +++ b/webapp/src/server/api/trpc.ts @@ -57,7 +57,7 @@ export const createTRPCContext = async (_opts: CreateNextContextOptions) => { }); const jwtCookie = - _opts.req.cookies[process.env.NEXT_PUBLIC_JWT_NAME as string]; + _opts.req.cookies[process.env.NEXT_PUBLIC_JWT_NAME ?? "cje-jwt"]; if (!jwtCookie) { return { diff --git a/webapp/src/styles/globals.css b/webapp/src/styles/globals.css index ed316524..af3161ca 100644 --- a/webapp/src/styles/globals.css +++ b/webapp/src/styles/globals.css @@ -112,3 +112,9 @@ a { color-scheme: dark; } } + +/* disable default tap color */ + +* { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0.05); +} diff --git a/webapp/src/utils/animations.ts b/webapp/src/utils/animations.ts new file mode 100644 index 00000000..741a86bf --- /dev/null +++ b/webapp/src/utils/animations.ts @@ -0,0 +1,84 @@ +import gsap, { Cubic } from 'gsap'; + +export const couponAnimation = (isSuccess: boolean, couponExists: boolean) => { + if (isSuccess) { + gsap.to('#coupon-code-icon', { + backgroundColor: '#42B918', + color: 'white', + duration: 1 + }); + + gsap.to('#coupon-code-icon-lock', { + display: 'none', + duration: 0, + delay: 0.5 + }); + + gsap.to('#coupon-code-icon-unlock', { + display: 'block', + duration: 0, + delay: 0.5 + }); + } + + gsap.to('#coupon-code-icon', { + opacity: couponExists ? 0 : 1, + duration: isSuccess ? 0.5 : 0, + delay: isSuccess ? 1.25 : 0 + }); + + gsap.to('#coupon-code-text', { + filter: couponExists ? 'blur(0px)' : 'blur(4.5px)', + duration: isSuccess ? 1 : 0, + delay: isSuccess ? 1.75 : 0 + }); + + if (isSuccess) { + gsap.fromTo( + '.coupon-info', + { + scaleY: 0, + opacity: 0, + transformOrigin: 'top' + }, + { + scaleY: 1, + transformOrigin: 'top', + opacity: 1, + ease: Cubic.easeIn, + duration: 0.75, + delay: 1.5 + } + ); + + gsap.fromTo( + '.btn-utils', + { + opacity: 0, + translateY: -35 + }, + { + opacity: 1, + translateY: 0, + duration: 1, + ease: Cubic.easeIn, + delay: 1.5 + } + ); + + gsap.fromTo( + '.btn-conditions', + { + translateY: -35, + delay: 1 + }, + { + opacity: 1, + duration: 1, + translateY: 0, + ease: Cubic.easeIn, + delay: 1.5 + } + ); + } +}; diff --git a/webapp/src/utils/chakra-theme.ts b/webapp/src/utils/chakra-theme.ts index 4210e100..f7ccc8fd 100644 --- a/webapp/src/utils/chakra-theme.ts +++ b/webapp/src/utils/chakra-theme.ts @@ -1,5 +1,9 @@ /* theme.ts */ -import { extendTheme } from "@chakra-ui/react"; +import { + StyleFunctionProps, + extendTheme, + theme as defaultTheme, +} from "@chakra-ui/react"; import localFont from "next/font/local"; export const Marianne = localFont({ @@ -37,18 +41,49 @@ export const Marianne = localFont({ ], }); +export const dottedPattern = (bgColor: string) => ({ + _after: { + content: `""`, + position: "absolute", + bottom: -2, // Adjust this value as needed to align with your design + left: 0, + right: 0, + height: "40px", // This is the height of the pseudo-element + backgroundImage: `radial-gradient(circle, ${bgColor} 8px, rgba(0, 0, 0, 0) 8px)`, // The color should be the same as the border color + backgroundSize: "23px 17px", // Adjust the size of the dots + backgroundRepeat: "repeat-x", + backgroundPosition: "bottom", + }, +}); + export const theme = extendTheme({ components: { Button: { sizes: { lg: { - px: "1.5rem", - borderRadius: "6.25rem", + borderRadius: "2xl", + py: 8, + fontWeight: "medium", + }, + sm: { + borderRadius: "lg", + py: 12, + fontWeight: "medium", + whiteSpace: "normal", }, }, defaultProps: { size: "lg", - colorScheme: "primary", + colorScheme: "blackBtn", + }, + variants: { + solid: (props: StyleFunctionProps) => ({ + "@media(hover: none)": { + _hover: { + bg: defaultTheme.components.Button.variants?.solid(props).bg, + }, + }, + }), }, }, }, @@ -65,7 +100,44 @@ export const theme = extendTheme({ "900": "#282a87", "950": "#18184e", }, - bgWhite: "#F7F7FA", + whiteBtn: { + "50": "#FFFFFF", + "100": "#FFFFFF", + "200": "#FFFFFF", + "300": "#FFFFFF", + "400": "#FFFFFF", + "500": "#FFFFFF", + "600": "#FFFFFF", + "700": "#FFFFFF", + "800": "#FFFFFF", + "900": "#FFFFFF", + }, + blackBtn: { + "50": "#000000", + "100": "#000000", + "200": "#000000", + "300": "#000000", + "400": "#000000", + "500": "#000000", + "600": "#000000", + "700": "#000000", + "800": "#000000", + "900": "#000000", + }, + "cje-gray": { + "50": "#f6f7f9", + "100": "#c5c7cb", + "200": "#cfd1d5", + "300": "#d9dbdf", + "400": "#e3e5e9", + "500": "#edeff3", + "600": "#f7f9fd", + "700": "#ffffff", + "800": "#ffffff", + "900": "#ffffff", + }, + success: "#42B918", + bgWhite: "#F7F7F7", disabled: "#9595B1", }, fonts: { diff --git a/webapp/src/utils/tools.ts b/webapp/src/utils/tools.ts new file mode 100644 index 00000000..b28ea34f --- /dev/null +++ b/webapp/src/utils/tools.ts @@ -0,0 +1,20 @@ +export const convertFrenchDateToEnglish = ( + frenchDate: string +): string | null => { + const match = frenchDate.match(/^(\d{2})\/(\d{2})\/(\d{2})$/); + + if (match) { + const [, day, month, year] = match.map(Number); + + if (year !== undefined && month !== undefined && day !== undefined) { + // Creating a Date object with a four-digit year + const englishFormattedDate = new Date(2000 + year, month - 1, day); + + // Using toISOString to get the date in ISO format (YYYY-MM-DD) + return englishFormattedDate.toISOString().split("T")[0] || null; + } + } + + // Return null if the input format is incorrect + return null; +}; diff --git a/webapp/tsconfig.json b/webapp/tsconfig.json index 27ec68b2..afc9a30d 100644 --- a/webapp/tsconfig.json +++ b/webapp/tsconfig.json @@ -1,6 +1,5 @@ { "compilerOptions": { - /* Base Options: */ "esModuleInterop": true, "skipLibCheck": true, "target": "es2022", @@ -8,13 +7,9 @@ "resolveJsonModule": true, "moduleDetection": "force", "isolatedModules": true, - - /* Strictness */ "strict": true, "noUncheckedIndexedAccess": true, "checkJs": true, - - /* Bundled projects */ "lib": ["dom", "dom.iterable", "ES2022"], "noEmit": true, "module": "ESNext", @@ -22,12 +17,8 @@ "jsx": "preserve", "plugins": [{ "name": "next" }], "incremental": true, - - /* Path Aliases */ "baseUrl": ".", - "paths": { - "~/*": ["./src/*"] - } + "paths": { "~/*": ["./src/*"] } }, "include": [ ".eslintrc.cjs", diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 3662b437..bcf5cb7a 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2318,6 +2318,18 @@ __metadata: languageName: node linkType: hard +"@chakra-ui/icons@npm:^2.1.1": + version: 2.1.1 + resolution: "@chakra-ui/icons@npm:2.1.1" + dependencies: + "@chakra-ui/icon": "npm:3.2.0" + peerDependencies: + "@chakra-ui/system": ">=2.0.0" + react: ">=18" + checksum: 36c94df62216f7445e49dfec98363f4c836e154ba118e6c446e774cade6f9e0e04c33a62fbe2ff9de26eb87f6a3ee805f8c7fd2ddc82c8ef2744994443a83b33 + languageName: node + linkType: hard + "@chakra-ui/image@npm:2.1.0": version: 2.1.0 resolution: "@chakra-ui/image@npm:2.1.0" @@ -3228,6 +3240,59 @@ __metadata: languageName: node linkType: hard +"@cloudflare/kv-asset-handler@npm:^0.2.0": + version: 0.2.0 + resolution: "@cloudflare/kv-asset-handler@npm:0.2.0" + dependencies: + mime: "npm:^3.0.0" + checksum: ec9816bb2901edff26e932566e77acff56c0a63927d78be6f05e44977209f0632103a802ba1498a2592bfc34c1e1502edba3644e8ff21ca10b201783ac5ff883 + languageName: node + linkType: hard + +"@cloudflare/workerd-darwin-64@npm:1.20231218.0": + version: 1.20231218.0 + resolution: "@cloudflare/workerd-darwin-64@npm:1.20231218.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-darwin-arm64@npm:1.20231218.0": + version: 1.20231218.0 + resolution: "@cloudflare/workerd-darwin-arm64@npm:1.20231218.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-64@npm:1.20231218.0": + version: 1.20231218.0 + resolution: "@cloudflare/workerd-linux-64@npm:1.20231218.0" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + +"@cloudflare/workerd-linux-arm64@npm:1.20231218.0": + version: 1.20231218.0 + resolution: "@cloudflare/workerd-linux-arm64@npm:1.20231218.0" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + +"@cloudflare/workerd-windows-64@npm:1.20231218.0": + version: 1.20231218.0 + resolution: "@cloudflare/workerd-windows-64@npm:1.20231218.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@cspotcode/source-map-support@npm:0.8.1": + version: 0.8.1 + resolution: "@cspotcode/source-map-support@npm:0.8.1" + dependencies: + "@jridgewell/trace-mapping": "npm:0.3.9" + checksum: 05c5368c13b662ee4c122c7bfbe5dc0b613416672a829f3e78bc49a357a197e0218d6e74e7c66cfcd04e15a179acab080bd3c69658c9fbefd0e1ccd950a07fc6 + languageName: node + linkType: hard + "@csstools/cascade-layer-name-parser@npm:^1.0.5, @csstools/cascade-layer-name-parser@npm:^1.0.7": version: 1.0.7 resolution: "@csstools/cascade-layer-name-parser@npm:1.0.7" @@ -3670,10 +3735,10 @@ __metadata: languageName: node linkType: hard -"@drizzle-team/studio@npm:^0.0.5": - version: 0.0.5 - resolution: "@drizzle-team/studio@npm:0.0.5" - checksum: f6508128ba53baab231891ebe479f1828d120137a5aa07c5b599bfd2cf313eaec0c1cfa017fd6d88eb1652a1436353db07116c23bf15174f3ae829841d060b6d +"@drizzle-team/studio@npm:^0.0.27": + version: 0.0.27 + resolution: "@drizzle-team/studio@npm:0.0.27" + checksum: 6af2e00672c4842566ec9fcca7feac355b03d6670c09030bac42adebcc45f97e99aaf80d3f8fad63ee5aa6e7fb85354e3eb83eb295965e63f2a2bc8a3f5562cd languageName: node linkType: hard @@ -3900,6 +3965,27 @@ __metadata: languageName: node linkType: hard +"@esbuild-plugins/node-globals-polyfill@npm:^0.2.3": + version: 0.2.3 + resolution: "@esbuild-plugins/node-globals-polyfill@npm:0.2.3" + peerDependencies: + esbuild: "*" + checksum: da3591b3943076a8d4a78320c176f37e5a5802512e2c3a792d4dfe495c051e097668dc56513160147b43e86987078559490164905ef41d1326ac0a9e7a6498ac + languageName: node + linkType: hard + +"@esbuild-plugins/node-modules-polyfill@npm:^0.2.2": + version: 0.2.2 + resolution: "@esbuild-plugins/node-modules-polyfill@npm:0.2.2" + dependencies: + escape-string-regexp: "npm:^4.0.0" + rollup-plugin-node-polyfills: "npm:^0.2.1" + peerDependencies: + esbuild: "*" + checksum: 8573eb409d19769ea6a2f621d8d7e344d84a9f19d03f37f4ace053e23dab8eeea08feea871c1704a2d39c0859adadfba808b59a50de4d227cb3879dbd90e7f52 + languageName: node + linkType: hard + "@esbuild/aix-ppc64@npm:0.19.11": version: 0.19.11 resolution: "@esbuild/aix-ppc64@npm:0.19.11" @@ -3907,6 +3993,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-arm64@npm:0.17.19" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/android-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm64@npm:0.18.20" @@ -3921,6 +4014,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-arm@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-arm@npm:0.17.19" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + "@esbuild/android-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-arm@npm:0.18.20" @@ -3935,6 +4035,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/android-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/android-x64@npm:0.17.19" + conditions: os=android & cpu=x64 + languageName: node + linkType: hard + "@esbuild/android-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/android-x64@npm:0.18.20" @@ -3949,6 +4056,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/darwin-arm64@npm:0.17.19" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/darwin-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-arm64@npm:0.18.20" @@ -3963,6 +4077,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/darwin-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/darwin-x64@npm:0.17.19" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + "@esbuild/darwin-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/darwin-x64@npm:0.18.20" @@ -3977,6 +4098,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/freebsd-arm64@npm:0.17.19" + conditions: os=freebsd & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/freebsd-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-arm64@npm:0.18.20" @@ -3991,6 +4119,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/freebsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/freebsd-x64@npm:0.17.19" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/freebsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/freebsd-x64@npm:0.18.20" @@ -4005,6 +4140,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-arm64@npm:0.17.19" + conditions: os=linux & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/linux-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm64@npm:0.18.20" @@ -4019,6 +4161,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-arm@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-arm@npm:0.17.19" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + "@esbuild/linux-arm@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-arm@npm:0.18.20" @@ -4033,6 +4182,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ia32@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-ia32@npm:0.17.19" + conditions: os=linux & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/linux-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ia32@npm:0.18.20" @@ -4047,6 +4203,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-loong64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-loong64@npm:0.17.19" + conditions: os=linux & cpu=loong64 + languageName: node + linkType: hard + "@esbuild/linux-loong64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-loong64@npm:0.18.20" @@ -4061,6 +4224,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-mips64el@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-mips64el@npm:0.17.19" + conditions: os=linux & cpu=mips64el + languageName: node + linkType: hard + "@esbuild/linux-mips64el@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-mips64el@npm:0.18.20" @@ -4075,6 +4245,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-ppc64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-ppc64@npm:0.17.19" + conditions: os=linux & cpu=ppc64 + languageName: node + linkType: hard + "@esbuild/linux-ppc64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-ppc64@npm:0.18.20" @@ -4089,6 +4266,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-riscv64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-riscv64@npm:0.17.19" + conditions: os=linux & cpu=riscv64 + languageName: node + linkType: hard + "@esbuild/linux-riscv64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-riscv64@npm:0.18.20" @@ -4103,6 +4287,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-s390x@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-s390x@npm:0.17.19" + conditions: os=linux & cpu=s390x + languageName: node + linkType: hard + "@esbuild/linux-s390x@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-s390x@npm:0.18.20" @@ -4117,6 +4308,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/linux-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/linux-x64@npm:0.17.19" + conditions: os=linux & cpu=x64 + languageName: node + linkType: hard + "@esbuild/linux-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/linux-x64@npm:0.18.20" @@ -4131,6 +4329,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/netbsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/netbsd-x64@npm:0.17.19" + conditions: os=netbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/netbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/netbsd-x64@npm:0.18.20" @@ -4145,6 +4350,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/openbsd-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/openbsd-x64@npm:0.17.19" + conditions: os=openbsd & cpu=x64 + languageName: node + linkType: hard + "@esbuild/openbsd-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/openbsd-x64@npm:0.18.20" @@ -4159,6 +4371,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/sunos-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/sunos-x64@npm:0.17.19" + conditions: os=sunos & cpu=x64 + languageName: node + linkType: hard + "@esbuild/sunos-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/sunos-x64@npm:0.18.20" @@ -4173,6 +4392,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-arm64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-arm64@npm:0.17.19" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + "@esbuild/win32-arm64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-arm64@npm:0.18.20" @@ -4187,6 +4413,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-ia32@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-ia32@npm:0.17.19" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + "@esbuild/win32-ia32@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-ia32@npm:0.18.20" @@ -4201,6 +4434,13 @@ __metadata: languageName: node linkType: hard +"@esbuild/win32-x64@npm:0.17.19": + version: 0.17.19 + resolution: "@esbuild/win32-x64@npm:0.17.19" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + "@esbuild/win32-x64@npm:0.18.20": version: 0.18.20 resolution: "@esbuild/win32-x64@npm:0.18.20" @@ -4250,6 +4490,13 @@ __metadata: languageName: node linkType: hard +"@fastify/busboy@npm:^2.0.0": + version: 2.1.0 + resolution: "@fastify/busboy@npm:2.1.0" + checksum: 7bb641080aac7cf01d88749ad331af10ba9ec3713ec07cabbe833908c75df21bd56249bb6173bdec07f5a41896b21e3689316f86684c06635da45f91ff4565a2 + languageName: node + linkType: hard + "@floating-ui/core@npm:^1.5.3": version: 1.5.3 resolution: "@floating-ui/core@npm:1.5.3" @@ -4276,6 +4523,16 @@ __metadata: languageName: node linkType: hard +"@gsap/react@npm:^2.1.0": + version: 2.1.0 + resolution: "@gsap/react@npm:2.1.0" + dependencies: + gsap: "npm:^3.12.4" + react: "npm:>=16" + checksum: 00ec2d2e312aae8ba56d323e373b70255e67b3825490c92087a18b02405711b31cfe7569e8c9eaf342b5b5c89e6716c6e16e1825ff751aa834c3ce9134f95b73 + languageName: node + linkType: hard + "@hapi/hoek@npm:^9.0.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -4531,7 +4788,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:^3.1.0": +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": version: 3.1.1 resolution: "@jridgewell/resolve-uri@npm:3.1.1" checksum: 0dbc9e29bc640bbbdc5b9876d2859c69042bfcf1423c1e6421bcca53e826660bff4e41c7d4bcb8dbea696404231a6f902f76ba41835d049e20f2dd6cffb713bf @@ -4562,6 +4819,16 @@ __metadata: languageName: node linkType: hard +"@jridgewell/trace-mapping@npm:0.3.9": + version: 0.3.9 + resolution: "@jridgewell/trace-mapping@npm:0.3.9" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.0.3" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + checksum: fa425b606d7c7ee5bfa6a31a7b050dd5814b4082f318e0e4190f991902181b4330f43f4805db1dd4f2433fd0ed9cc7a7b9c2683f1deeab1df1b0a98b1e24055b + languageName: node + linkType: hard + "@jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.20, @jridgewell/trace-mapping@npm:^0.3.9": version: 0.3.20 resolution: "@jridgewell/trace-mapping@npm:0.3.20" @@ -4863,21 +5130,21 @@ __metadata: languageName: node linkType: hard -"@payloadcms/db-postgres@npm:^0.3.0": - version: 0.3.0 - resolution: "@payloadcms/db-postgres@npm:0.3.0" +"@payloadcms/db-postgres@npm:^0.4.0": + version: 0.4.0 + resolution: "@payloadcms/db-postgres@npm:0.4.0" dependencies: "@libsql/client": "npm:^0.3.1" console-table-printer: "npm:2.11.2" - drizzle-kit: "npm:0.19.13-e99bac1" - drizzle-orm: "npm:0.28.5" + drizzle-kit: "npm:0.20.5-608ae62" + drizzle-orm: "npm:0.29.3" pg: "npm:8.11.3" prompts: "npm:2.4.2" to-snake-case: "npm:1.0.0" uuid: "npm:9.0.0" peerDependencies: payload: ^2.0.0 - checksum: 12fb0538918170a9b6993e861dcd4a2b6cb1a3af453799b0ada6014597c8ada3ed066b50f8b2e537fcdf3cb0da903dbede37b4e533583e2c494c6f12053a857e + checksum: f9eb8a9a78b881a65b494d391e0e20a6b73674d335929f5b36f1cf45cd68209582172e061a5bd19dd7b683266e06ec4496a197cbe70408cd2ab6119ac4f9b079 languageName: node linkType: hard @@ -6120,6 +6387,15 @@ __metadata: languageName: node linkType: hard +"@types/node-forge@npm:^1.3.0": + version: 1.3.11 + resolution: "@types/node-forge@npm:1.3.11" + dependencies: + "@types/node": "npm:*" + checksum: 3d7d23ca0ba38ac0cf74028393bd70f31169ab9aba43f21deb787840170d307d662644bac07287495effe2812ddd7ac8a14dbd43f16c2936bbb06312e96fc3b9 + languageName: node + linkType: hard + "@types/node@npm:*, @types/node@npm:^20": version: 20.11.0 resolution: "@types/node@npm:20.11.0" @@ -6136,6 +6412,15 @@ __metadata: languageName: node linkType: hard +"@types/papaparse@npm:^5": + version: 5.3.14 + resolution: "@types/papaparse@npm:5.3.14" + dependencies: + "@types/node": "npm:*" + checksum: feb4d215903b67442feaa9836a6a5771e78dc6a9da24781e399c6f891622fa82245cd783ab2613c5be43e4a2d6a94da52325538e4485af258166864576ecd0d8 + languageName: node + linkType: hard + "@types/parse-json@npm:^4.0.0": version: 4.0.2 resolution: "@types/parse-json@npm:4.0.2" @@ -6490,14 +6775,14 @@ __metadata: languageName: node linkType: hard -"acorn-walk@npm:^8.0.0": +"acorn-walk@npm:^8.0.0, acorn-walk@npm:^8.2.0": version: 8.3.2 resolution: "acorn-walk@npm:8.3.2" checksum: 7e2a8dad5480df7f872569b9dccff2f3da7e65f5353686b1d6032ab9f4ddf6e3a2cb83a9b52cf50b1497fd522154dda92f0abf7153290cc79cd14721ff121e52 languageName: node linkType: hard -"acorn@npm:^8.0.4, acorn@npm:^8.7.1, acorn@npm:^8.8.2": +"acorn@npm:^8.0.4, acorn@npm:^8.7.1, acorn@npm:^8.8.0, acorn@npm:^8.8.2": version: 8.11.3 resolution: "acorn@npm:8.11.3" bin: @@ -6712,6 +6997,15 @@ __metadata: languageName: node linkType: hard +"as-table@npm:^1.0.36": + version: 1.0.55 + resolution: "as-table@npm:1.0.55" + dependencies: + printable-characters: "npm:^1.0.42" + checksum: 8c5693a84621fe53c62fcad6b779dc55c5caf4d43b8e67077964baea4a337769ef53f590d7395c806805b4ef1a391b614ba9acdee19b2ca4309ddedaf13894e6 + languageName: node + linkType: hard + "async@npm:^3.2.3": version: 3.2.5 resolution: "async@npm:3.2.5" @@ -6891,6 +7185,13 @@ __metadata: languageName: node linkType: hard +"blake3-wasm@npm:^2.1.5": + version: 2.1.5 + resolution: "blake3-wasm@npm:2.1.5" + checksum: 5dc729d8e3a9d1d7ab016b36cdda264a327ada0239716df48435163e11d2bf6df25d6e421655a1f52649098ae49555268a654729b7d02768f77c571ab37ef814 + languageName: node + linkType: hard + "body-parser@npm:1.20.1": version: 1.20.1 resolution: "body-parser@npm:1.20.1" @@ -7170,6 +7471,16 @@ __metadata: languageName: node linkType: hard +"capnp-ts@npm:^0.7.0": + version: 0.7.0 + resolution: "capnp-ts@npm:0.7.0" + dependencies: + debug: "npm:^4.3.1" + tslib: "npm:^2.2.0" + checksum: 83d559c3d59126ee39295973bf2e9228cd4b559c81bfc938268c63deba4020f0df6ce2f2d1e2b7d7e4421de21f4854424b774ab9ac4d9a66d1c57d2fef7da870 + languageName: node + linkType: hard + "chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -7205,7 +7516,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:>=3.0.0 <4.0.0": +"chokidar@npm:>=3.0.0 <4.0.0, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -7579,7 +7890,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.5.0": +"cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" checksum: c01ca3ef8d7b8187bae434434582288681273b5a9ed27521d4d7f9f7928fe0c920df0decd9f9d3bbd2d14ac432b8c8cf42b98b3bdd5bfe0e6edddeebebe8b61d @@ -7945,6 +8256,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^2.0.0": + version: 2.0.2 + resolution: "data-uri-to-buffer@npm:2.0.2" + checksum: 341b6191ed65fa453e97a6d44db06082121ebc2ef3e6e096dfb6a1ebbc75e8be39d4199a5b4dba0f0efc43f2a3b2bcc276d85cf1407eba880eb09ebf17c3c31e + languageName: node + linkType: hard + "data-uri-to-buffer@npm:^4.0.0": version: 4.0.1 resolution: "data-uri-to-buffer@npm:4.0.1" @@ -8313,11 +8631,11 @@ __metadata: languageName: node linkType: hard -"drizzle-kit@npm:0.19.13-e99bac1": - version: 0.19.13-e99bac1 - resolution: "drizzle-kit@npm:0.19.13-e99bac1" +"drizzle-kit@npm:0.20.5-608ae62": + version: 0.20.5-608ae62 + resolution: "drizzle-kit@npm:0.20.5-608ae62" dependencies: - "@drizzle-team/studio": "npm:^0.0.5" + "@drizzle-team/studio": "npm:^0.0.27" "@esbuild-kit/esm-loader": "npm:^2.5.5" camelcase: "npm:^7.0.1" chalk: "npm:^5.2.0" @@ -8328,16 +8646,17 @@ __metadata: hanji: "npm:^0.0.5" json-diff: "npm:0.9.0" minimatch: "npm:^7.4.3" + wrangler: "npm:^3.7.0" zod: "npm:^3.20.2" bin: - drizzle-kit: index.cjs - checksum: 7df9775011305d090b0a3ec606cc44b9ba94c7884fb2d25f4acbaaf5aaa9ca92ed750071725714d5d4670ea16183e0c263a731d550f236551272b5a656a6a78f + drizzle-kit: bin.cjs + checksum: e1943056cc1bd6c09215b3cd5ad3f78a63604c964960cc67a018ceffc71dfd43a7dd4e9f6a5b8a34ef3ad5053862f9e516f47f0a8bf6fb365fbde9c295ce0836 languageName: node linkType: hard -"drizzle-orm@npm:0.28.5": - version: 0.28.5 - resolution: "drizzle-orm@npm:0.28.5" +"drizzle-orm@npm:0.29.3": + version: 0.29.3 + resolution: "drizzle-orm@npm:0.29.3" peerDependencies: "@aws-sdk/client-rds-data": ">=3" "@cloudflare/workers-types": ">=3" @@ -8347,15 +8666,18 @@ __metadata: "@planetscale/database": ">=1" "@types/better-sqlite3": "*" "@types/pg": "*" + "@types/react": ">=18" "@types/sql.js": "*" "@vercel/postgres": "*" better-sqlite3: ">=7" bun-types: "*" + expo-sqlite: ">=13.2.0" knex: "*" kysely: "*" mysql2: ">=2" pg: ">=8" postgres: ">=3" + react: ">=18" sql.js: ">=1" sqlite3: ">=5" peerDependenciesMeta: @@ -8375,6 +8697,8 @@ __metadata: optional: true "@types/pg": optional: true + "@types/react": + optional: true "@types/sql.js": optional: true "@vercel/postgres": @@ -8383,6 +8707,8 @@ __metadata: optional: true bun-types: optional: true + expo-sqlite: + optional: true knex: optional: true kysely: @@ -8393,11 +8719,13 @@ __metadata: optional: true postgres: optional: true + react: + optional: true sql.js: optional: true sqlite3: optional: true - checksum: e62deb7caa8ca9c71824ab10ab06b3212bb5205561c3474504298554fee16d5a76c6731ae2e0f3955f150eb839bdd78a5750e1f0fe40d1c3ffcdc04140ed4519 + checksum: a5a53e4599981f256e10bd0e6e4724f14ffe7c9f6bef963c5cc9d00c8379c2367324ae0c67abc52a319704fedba796a5583dac3d36fac0de19585f87844d3611 languageName: node linkType: hard @@ -8723,6 +9051,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:0.17.19": + version: 0.17.19 + resolution: "esbuild@npm:0.17.19" + dependencies: + "@esbuild/android-arm": "npm:0.17.19" + "@esbuild/android-arm64": "npm:0.17.19" + "@esbuild/android-x64": "npm:0.17.19" + "@esbuild/darwin-arm64": "npm:0.17.19" + "@esbuild/darwin-x64": "npm:0.17.19" + "@esbuild/freebsd-arm64": "npm:0.17.19" + "@esbuild/freebsd-x64": "npm:0.17.19" + "@esbuild/linux-arm": "npm:0.17.19" + "@esbuild/linux-arm64": "npm:0.17.19" + "@esbuild/linux-ia32": "npm:0.17.19" + "@esbuild/linux-loong64": "npm:0.17.19" + "@esbuild/linux-mips64el": "npm:0.17.19" + "@esbuild/linux-ppc64": "npm:0.17.19" + "@esbuild/linux-riscv64": "npm:0.17.19" + "@esbuild/linux-s390x": "npm:0.17.19" + "@esbuild/linux-x64": "npm:0.17.19" + "@esbuild/netbsd-x64": "npm:0.17.19" + "@esbuild/openbsd-x64": "npm:0.17.19" + "@esbuild/sunos-x64": "npm:0.17.19" + "@esbuild/win32-arm64": "npm:0.17.19" + "@esbuild/win32-ia32": "npm:0.17.19" + "@esbuild/win32-x64": "npm:0.17.19" + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: c7ac14bfaaebe4745d5d18347b4f6854fd1140acb9389e88dbfa5c20d4e2122451d9647d5498920470a880a605d6e5502b5c2102da6c282b01f129ddd49d2874 + languageName: node + linkType: hard + "esbuild@npm:^0.18.6, esbuild@npm:~0.18.20": version: 0.18.20 resolution: "esbuild@npm:0.18.20" @@ -8941,6 +9346,13 @@ __metadata: languageName: node linkType: hard +"estree-walker@npm:^0.6.1": + version: 0.6.1 + resolution: "estree-walker@npm:0.6.1" + checksum: 6dabc855faa04a1ffb17b6a9121b6008ba75ab5a163ad9dc3d7fca05cfda374c5f5e91418d783496620ca75e99a73c40874d8b75f23b4117508cc8bde78e7b41 + languageName: node + linkType: hard + "estree-walker@npm:^1.0.1": version: 1.0.1 resolution: "estree-walker@npm:1.0.1" @@ -8986,6 +9398,13 @@ __metadata: languageName: node linkType: hard +"exit-hook@npm:^2.2.1": + version: 2.2.1 + resolution: "exit-hook@npm:2.2.1" + checksum: 0803726d1b60aade6afd10c73e5a7e1bf256ac9bee78362a88e91a4f735e8c67899f2853ddc613072c05af07bbb067a9978a740e614db1aeef167d50c6dc5c09 + languageName: node + linkType: hard + "expand-template@npm:^2.0.3": version: 2.0.3 resolution: "expand-template@npm:2.0.3" @@ -9545,6 +9964,16 @@ __metadata: languageName: node linkType: hard +"get-source@npm:^2.0.12": + version: 2.0.12 + resolution: "get-source@npm:2.0.12" + dependencies: + data-uri-to-buffer: "npm:^2.0.0" + source-map: "npm:^0.6.1" + checksum: b1db46d28902344fd9407e1f0ed0b8f3a85cb4650f85ba8cee9c0b422fc75118172f12f735706e2c6e034617b13a2fbc5266e7fab617ecb184f0cee074b9dd3e + languageName: node + linkType: hard + "get-stdin@npm:^8.0.0": version: 8.0.0 resolution: "get-stdin@npm:8.0.0" @@ -9792,6 +10221,13 @@ __metadata: languageName: node linkType: hard +"gsap@npm:^3.12.4, gsap@npm:^3.12.5": + version: 3.12.5 + resolution: "gsap@npm:3.12.5" + checksum: cb38e89bd577fc18c9cf77e4aeb83617603b1e0fa768fe8ca1acd836fd852936722e186a88c9cf6ece42e53407edd45324be2bfe35a41f424c5f2c2b33fd1a98 + languageName: node + linkType: hard + "gzip-size@npm:^6.0.0": version: 6.0.0 resolution: "gzip-size@npm:6.0.0" @@ -11266,7 +11702,7 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.25.0, magic-string@npm:^0.25.7": +"magic-string@npm:^0.25.0, magic-string@npm:^0.25.3, magic-string@npm:^0.25.7": version: 0.25.9 resolution: "magic-string@npm:0.25.9" dependencies: @@ -11447,6 +11883,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^3.0.0": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: 402e792a8df1b2cc41cb77f0dcc46472b7944b7ec29cb5bbcd398624b6b97096728f1239766d3fdeb20551dd8d94738344c195a6ea10c4f906eb0356323b0531 + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -11481,6 +11926,28 @@ __metadata: languageName: node linkType: hard +"miniflare@npm:3.20231218.3": + version: 3.20231218.3 + resolution: "miniflare@npm:3.20231218.3" + dependencies: + "@cspotcode/source-map-support": "npm:0.8.1" + acorn: "npm:^8.8.0" + acorn-walk: "npm:^8.2.0" + capnp-ts: "npm:^0.7.0" + exit-hook: "npm:^2.2.1" + glob-to-regexp: "npm:^0.4.1" + stoppable: "npm:^1.1.0" + undici: "npm:^5.28.2" + workerd: "npm:1.20231218.0" + ws: "npm:^8.11.0" + youch: "npm:^3.2.2" + zod: "npm:^3.20.6" + bin: + miniflare: bootstrap.js + checksum: 89cd180344cdc425e851a035ff164965aa708541f98c9cb81a5679c95d40c58d37c85b08dd0627a1574872d77543a7c43a67779fd62c5e94373ae80fe4bafa71 + languageName: node + linkType: hard + "minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -11699,6 +12166,15 @@ __metadata: languageName: node linkType: hard +"mustache@npm:^4.2.0": + version: 4.2.0 + resolution: "mustache@npm:4.2.0" + bin: + mustache: bin/mustache + checksum: 1f8197e8a19e63645a786581d58c41df7853da26702dbc005193e2437c98ca49b255345c173d50c08fe4b4dbb363e53cb655ecc570791f8deb09887248dd34a2 + languageName: node + linkType: hard + "mz@npm:^2.7.0": version: 2.7.0 resolution: "mz@npm:2.7.0" @@ -11710,7 +12186,7 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": +"nanoid@npm:^3.3.3, nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": version: 3.3.7 resolution: "nanoid@npm:3.3.7" bin: @@ -11875,6 +12351,13 @@ __metadata: languageName: node linkType: hard +"node-forge@npm:^1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: e882819b251a4321f9fc1d67c85d1501d3004b4ee889af822fd07f64de3d1a8e272ff00b689570af0465d65d6bf5074df9c76e900e0aff23e60b847f2a46fbe8 + languageName: node + linkType: hard + "node-gyp@npm:latest": version: 10.0.1 resolution: "node-gyp@npm:10.0.1" @@ -12110,6 +12593,13 @@ __metadata: languageName: node linkType: hard +"papaparse@npm:^5.4.1": + version: 5.4.1 + resolution: "papaparse@npm:5.4.1" + checksum: 201f37c4813453fed5bfb4c01816696b099d2db9ff1e8fb610acc4771fdde91d2a22b6094721edb0fedb21ca3c46f04263f68be4beb3e35b8c72278f0cedc7b7 + languageName: node + linkType: hard + "param-case@npm:^3.0.4": version: 3.0.4 resolution: "param-case@npm:3.0.4" @@ -12296,6 +12786,13 @@ __metadata: languageName: node linkType: hard +"path-to-regexp@npm:^6.2.0": + version: 6.2.1 + resolution: "path-to-regexp@npm:6.2.1" + checksum: 7a73811ca703e5c199e5b50b9649ab8f6f7b458a37f7dff9ea338815203f5b1f95fe8cb24d4fdfe2eab5d67ce43562d92534330babca35cdf3231f966adb9360 + languageName: node + linkType: hard + "path-type@npm:^4.0.0": version: 4.0.0 resolution: "path-type@npm:4.0.0" @@ -12310,9 +12807,9 @@ __metadata: languageName: node linkType: hard -"payload@npm:^2.6.0": - version: 2.7.0 - resolution: "payload@npm:2.7.0" +"payload@npm:^2.8.2": + version: 2.8.2 + resolution: "payload@npm:2.8.2" dependencies: "@date-io/date-fns": "npm:2.16.0" "@dnd-kit/core": "npm:6.0.8" @@ -12405,7 +12902,7 @@ __metadata: uuid: "npm:9.0.1" bin: payload: bin.js - checksum: 40f226a37c1a81a8fb1c5c88855588163975e24f55efa24f25e66fae2720279339d44f035ed5397b5a8111a93f902e178218f1b4eaf7e57e04d4fa3844006ed5 + checksum: 10fb2fc0cc5fcff2b0a633d960c9209a91e6383935e0baa4121fa933098dc41f3ceac41afcc4b881105efc5c8be8a3462b5e292e4278068ea5b77f186e81e6ee languageName: node linkType: hard @@ -13483,6 +13980,13 @@ __metadata: languageName: node linkType: hard +"printable-characters@npm:^1.0.42": + version: 1.0.42 + resolution: "printable-characters@npm:1.0.42" + checksum: 7c94d94c6041a37c385af770c7402ad5a2e8a3429ca4d2505a9f19fde39bac9a8fd1edfbfa02f1eae5b4b0f3536b6b8ee6c84621f7c0fcb41476b2df6ee20e4b + languageName: node + linkType: hard + "probe-image-size@npm:6.0.0": version: 6.0.0 resolution: "probe-image-size@npm:6.0.0" @@ -14036,7 +14540,7 @@ __metadata: languageName: node linkType: hard -"react@npm:18.2.0, react@npm:^18": +"react@npm:18.2.0, react@npm:>=16, react@npm:^18": version: 18.2.0 resolution: "react@npm:18.2.0" dependencies: @@ -14267,7 +14771,14 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.9.0": +"resolve.exports@npm:^2.0.2": + version: 2.0.2 + resolution: "resolve.exports@npm:2.0.2" + checksum: cc4cffdc25447cf34730f388dca5021156ba9302a3bad3d7f168e790dc74b2827dff603f1bc6ad3d299bac269828dca96dd77e036dc9fba6a2a1807c47ab5c98 + languageName: node + linkType: hard + +"resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.22.8, resolve@npm:^1.9.0": version: 1.22.8 resolution: "resolve@npm:1.22.8" dependencies: @@ -14280,7 +14791,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.9.0#optional!builtin": +"resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin, resolve@patch:resolve@npm%3A^1.9.0#optional!builtin": version: 1.22.8 resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" dependencies: @@ -14325,6 +14836,26 @@ __metadata: languageName: node linkType: hard +"rollup-plugin-inject@npm:^3.0.0": + version: 3.0.2 + resolution: "rollup-plugin-inject@npm:3.0.2" + dependencies: + estree-walker: "npm:^0.6.1" + magic-string: "npm:^0.25.3" + rollup-pluginutils: "npm:^2.8.1" + checksum: 35b9d955039b56b43750a9e458bb51b7956b048b6d3ca57b1f03462aa5a0cb176d1b677d95e909b64eee4e9adf73c02f569ad8c0ab5aafdec818ff51700c114c + languageName: node + linkType: hard + +"rollup-plugin-node-polyfills@npm:^0.2.1": + version: 0.2.1 + resolution: "rollup-plugin-node-polyfills@npm:0.2.1" + dependencies: + rollup-plugin-inject: "npm:^3.0.0" + checksum: 30f9e09cbbf979b1212e0c455d74c3a061994fc19ddf160da4634b11377222cea5903a5ba05db66be849f550cde9ffc80ecbfcfb48544045d08bfc408501417d + languageName: node + linkType: hard + "rollup-plugin-terser@npm:^7.0.0": version: 7.0.2 resolution: "rollup-plugin-terser@npm:7.0.2" @@ -14339,6 +14870,15 @@ __metadata: languageName: node linkType: hard +"rollup-pluginutils@npm:^2.8.1": + version: 2.8.2 + resolution: "rollup-pluginutils@npm:2.8.2" + dependencies: + estree-walker: "npm:^0.6.1" + checksum: 20947bec5a5dd68b5c5c8423911e6e7c0ad834c451f1a929b1f4e2bc08836ad3f1a722ef2bfcbeca921870a0a283f13f064a317dc7a6768496e98c9a641ba290 + languageName: node + linkType: hard + "rollup@npm:^2.43.1": version: 2.79.1 resolution: "rollup@npm:2.79.1" @@ -14535,6 +15075,16 @@ __metadata: languageName: node linkType: hard +"selfsigned@npm:^2.0.1": + version: 2.4.1 + resolution: "selfsigned@npm:2.4.1" + dependencies: + "@types/node-forge": "npm:^1.3.0" + node-forge: "npm:^1" + checksum: 521829ec36ea042f7e9963bf1da2ed040a815cf774422544b112ec53b7edc0bc50a0f8cc2ae7aa6cc19afa967c641fd96a15de0fc650c68651e41277d2e1df09 + languageName: node + linkType: hard + "semver@npm:7.5.4, semver@npm:^7.3.5, semver@npm:^7.3.8, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" @@ -14932,6 +15482,13 @@ __metadata: languageName: node linkType: hard +"source-map@npm:0.6.1, source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0, source-map@npm:~0.6.1": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + "source-map@npm:^0.5.7": version: 0.5.7 resolution: "source-map@npm:0.5.7" @@ -14939,13 +15496,6 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.6.0, source-map@npm:~0.6.0, source-map@npm:~0.6.1": - version: 0.6.1 - resolution: "source-map@npm:0.6.1" - checksum: ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 - languageName: node - linkType: hard - "source-map@npm:^0.8.0-beta.0": version: 0.8.0-beta.0 resolution: "source-map@npm:0.8.0-beta.0" @@ -14987,6 +15537,16 @@ __metadata: languageName: node linkType: hard +"stacktracey@npm:^2.1.8": + version: 2.1.8 + resolution: "stacktracey@npm:2.1.8" + dependencies: + as-table: "npm:^1.0.36" + get-source: "npm:^2.0.12" + checksum: e17357d0a532d303138899b910ab660572009a1f4cde1cbf73b99416957a2378e6e1c791b3c31b043cf7c5f37647da1dd114e66c9203f23c65b34f783665405b + languageName: node + linkType: hard + "state-local@npm:^1.0.6": version: 1.0.7 resolution: "state-local@npm:1.0.7" @@ -15010,6 +15570,13 @@ __metadata: languageName: node linkType: hard +"stoppable@npm:^1.1.0": + version: 1.1.0 + resolution: "stoppable@npm:1.1.0" + checksum: ba91b65e6442bf6f01ce837a727ece597a977ed92a05cb9aea6bf446c5e0dcbccc28f31b793afa8aedd8f34baaf3335398d35f903938d5493f7fbe386a1e090e + languageName: node + linkType: hard + "stream-browserify@npm:3.0.0": version: 3.0.0 resolution: "stream-browserify@npm:3.0.0" @@ -15667,7 +16234,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0": +"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.5.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb @@ -15823,6 +16390,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.28.2": + version: 5.28.2 + resolution: "undici@npm:5.28.2" + dependencies: + "@fastify/busboy": "npm:^2.0.0" + checksum: 34385ad9b3ba85309972ee3c1b426dcd19b94a5a6aa9c54499b5f48436c0ecc13a9b1e756a7c6a953eaefa9f4263890625ece5f2719fd774b0852204f5e4d5f9 + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -16129,13 +16705,15 @@ __metadata: dependencies: "@aws-sdk/client-s3": "npm:^3.490.0" "@aws-sdk/lib-storage": "npm:^3.490.0" + "@chakra-ui/icons": "npm:^2.1.1" "@chakra-ui/next-js": "npm:^2.2.0" "@chakra-ui/react": "npm:^2.8.2" "@ducanh2912/next-pwa": "npm:^10.1.0" "@emotion/react": "npm:^11.11.1" "@emotion/styled": "npm:^11.11.0" + "@gsap/react": "npm:^2.1.0" "@payloadcms/bundler-webpack": "npm:^1.0.5" - "@payloadcms/db-postgres": "npm:^0.3.0" + "@payloadcms/db-postgres": "npm:^0.4.0" "@payloadcms/next-payload": "npm:^0.1.11" "@payloadcms/plugin-cloud-storage": "npm:^1.1.1" "@payloadcms/richtext-slate": "npm:^1.3.1" @@ -16146,16 +16724,19 @@ __metadata: "@trpc/react-query": "npm:^10.44.1" "@trpc/server": "npm:^10.44.1" "@types/node": "npm:^20" + "@types/papaparse": "npm:^5" "@types/react": "npm:^18" "@types/react-dom": "npm:^18" aws-crt: "npm:^1.20.1" cookies-next: "npm:^4.1.0" dotenv: "npm:^16.3.1" framer-motion: "npm:^10.16.16" + gsap: "npm:^3.12.5" ignore-styles: "npm:^5.0.1" jwt-decode: "npm:^4.0.0" next: "npm:13.5.5" - payload: "npm:^2.6.0" + papaparse: "npm:^5.4.1" + payload: "npm:^2.8.2" react: "npm:^18" react-dom: "npm:^18" react-hook-form: "npm:^7.49.2" @@ -16658,6 +17239,61 @@ __metadata: languageName: node linkType: hard +"workerd@npm:1.20231218.0": + version: 1.20231218.0 + resolution: "workerd@npm:1.20231218.0" + dependencies: + "@cloudflare/workerd-darwin-64": "npm:1.20231218.0" + "@cloudflare/workerd-darwin-arm64": "npm:1.20231218.0" + "@cloudflare/workerd-linux-64": "npm:1.20231218.0" + "@cloudflare/workerd-linux-arm64": "npm:1.20231218.0" + "@cloudflare/workerd-windows-64": "npm:1.20231218.0" + dependenciesMeta: + "@cloudflare/workerd-darwin-64": + optional: true + "@cloudflare/workerd-darwin-arm64": + optional: true + "@cloudflare/workerd-linux-64": + optional: true + "@cloudflare/workerd-linux-arm64": + optional: true + "@cloudflare/workerd-windows-64": + optional: true + bin: + workerd: bin/workerd + checksum: 67121048c4b2ca3d9a9ba5971a3f06f013792c39ec74be8a89d508460fe815677379ee0d3d6ee9272b9f670a38af35c6df4728059f15545d3421ceb8a5506e41 + languageName: node + linkType: hard + +"wrangler@npm:^3.7.0": + version: 3.24.0 + resolution: "wrangler@npm:3.24.0" + dependencies: + "@cloudflare/kv-asset-handler": "npm:^0.2.0" + "@esbuild-plugins/node-globals-polyfill": "npm:^0.2.3" + "@esbuild-plugins/node-modules-polyfill": "npm:^0.2.2" + blake3-wasm: "npm:^2.1.5" + chokidar: "npm:^3.5.3" + esbuild: "npm:0.17.19" + fsevents: "npm:~2.3.2" + miniflare: "npm:3.20231218.3" + nanoid: "npm:^3.3.3" + path-to-regexp: "npm:^6.2.0" + resolve: "npm:^1.22.8" + resolve.exports: "npm:^2.0.2" + selfsigned: "npm:^2.0.1" + source-map: "npm:0.6.1" + xxhash-wasm: "npm:^1.0.1" + dependenciesMeta: + fsevents: + optional: true + bin: + wrangler: bin/wrangler.js + wrangler2: bin/wrangler.js + checksum: 4c12568b8a8cc0450cf3dea44db22ef91d1e567b043d3cc49d029534b2dda1460283b35011a7d4ad0c14e2a1c78efcfda952e2bf5395c6233f361d0ea7289325 + languageName: node + linkType: hard + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version: 7.0.0 resolution: "wrap-ansi@npm:7.0.0" @@ -16687,7 +17323,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:*, ws@npm:^8.13.0": +"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.13.0": version: 8.16.0 resolution: "ws@npm:8.16.0" peerDependencies: @@ -16736,6 +17372,13 @@ __metadata: languageName: node linkType: hard +"xxhash-wasm@npm:^1.0.1": + version: 1.0.2 + resolution: "xxhash-wasm@npm:1.0.2" + checksum: 5ba899d9216d9897de2d61a5331b16c99226e75ce47895fc8c730bac5cb00e6e50856dd8f489c12b3012f0fc81b6894806b2e44d2eb3cc7843919793485a30d1 + languageName: node + linkType: hard + "yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1" @@ -16757,7 +17400,18 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.20.2, zod@npm:^3.22.4": +"youch@npm:^3.2.2": + version: 3.3.3 + resolution: "youch@npm:3.3.3" + dependencies: + cookie: "npm:^0.5.0" + mustache: "npm:^4.2.0" + stacktracey: "npm:^2.1.8" + checksum: 6e5dd4be39ea737fb557fe0647855ce7f0e2182330342cc5e2315a688e0950683967abbc43dd6452b80f39271b14872c390a6e96009f2475905d6be824d38109 + languageName: node + linkType: hard + +"zod@npm:^3.20.2, zod@npm:^3.20.6, zod@npm:^3.22.4": version: 3.22.4 resolution: "zod@npm:3.22.4" checksum: 7578ab283dac0eee66a0ad0fc4a7f28c43e6745aadb3a529f59a4b851aa10872b3890398b3160f257f4b6817b4ce643debdda4fb21a2c040adda7862cab0a587