Compare commits
54 Commits
add-next-a
...
main
Author | SHA1 | Date | |
---|---|---|---|
13f9d2647b | |||
94f70c2647 | |||
d65a81ccc9 | |||
51ecbd5b38 | |||
54831e55d4 | |||
c83769e6e0 | |||
2a41854483 | |||
209cb9eb16 | |||
9f086bd561 | |||
59c10140a8 | |||
2322c320a6 | |||
3064db8df7 | |||
e910515270 | |||
![]() |
832a77f109 | ||
dc1451bd2e | |||
5f9432a427 | |||
bef6bd691d | |||
a2c340cd15 | |||
a8d4a64b7b | |||
0e34734a45 | |||
5395028cc8 | |||
b5e6551692 | |||
2a3d310389 | |||
c487bbe003 | |||
92d18426b9 | |||
77a8137342 | |||
a02264b78b | |||
0d67624b91 | |||
a4c813069a | |||
b63e63203e | |||
4da489ba52 | |||
f39bfd9980 | |||
364a8a43c5 | |||
8cacef2268 | |||
![]() |
954c11101c | ||
![]() |
ee86d453ba | ||
![]() |
48e3eb49d2 | ||
![]() |
8efb1e045d | ||
![]() |
23e9f91323 | ||
![]() |
2ef07fd37a | ||
![]() |
f66c35a225 | ||
![]() |
8b42377453 | ||
![]() |
ee7268e724 | ||
![]() |
cad4b78f47 | ||
![]() |
634f35657e | ||
![]() |
35c301a686 | ||
![]() |
2edc5d57b6 | ||
![]() |
f7144e7cf4 | ||
![]() |
cd1dc2a555 | ||
![]() |
c243fda8e1 | ||
![]() |
7fc0895177 | ||
![]() |
6e4efe2842 | ||
![]() |
2e2f99e0ec | ||
![]() |
33b30264d9 |
8
.dockerignore
Normal file
8
.dockerignore
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.env
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
node_modules
|
||||||
|
npm-debug.log
|
||||||
|
README.md
|
||||||
|
.next
|
||||||
|
.git
|
@ -19,3 +19,6 @@ DATABASE_URL=
|
|||||||
|
|
||||||
# Admin account details
|
# Admin account details
|
||||||
ADMIN_PASSWORD="password"
|
ADMIN_PASSWORD="password"
|
||||||
|
|
||||||
|
# Container Port
|
||||||
|
PORT=1234
|
||||||
|
68
Dockerfile
68
Dockerfile
@ -1,19 +1,61 @@
|
|||||||
# Production Environment Dockerfile
|
FROM node:18-alpine AS base
|
||||||
|
|
||||||
# base image
|
# Install dependencies only when needed
|
||||||
FROM node:alpine
|
##### DEPENDENCIES
|
||||||
|
FROM base AS deps
|
||||||
# create & set working directory
|
RUN apk add --no-cache libc6-compat
|
||||||
RUN mkdir -p /app
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# copy source files
|
# Install Prisma Client - remove if not using Prisma
|
||||||
COPY . /app
|
|
||||||
|
|
||||||
# install dependencies
|
COPY prisma ./
|
||||||
RUN npm install
|
|
||||||
|
# Install dependencies based on the preferred package manager
|
||||||
|
|
||||||
|
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml\* ./
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||||
|
elif [ -f package-lock.json ]; then npm ci; \
|
||||||
|
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i; \
|
||||||
|
else echo "Lockfile not found." && exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
##### BUILDER
|
||||||
|
|
||||||
|
FROM base AS builder
|
||||||
|
ARG DATABASE_URL
|
||||||
|
ARG NEXT_PUBLIC_CLIENTVAR
|
||||||
|
WORKDIR /app
|
||||||
|
COPY --from=deps /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
if [ -f yarn.lock ]; then SKIP_ENV_VALIDATION=1 yarn build; \
|
||||||
|
elif [ -f package-lock.json ]; then SKIP_ENV_VALIDATION=1 npm run build; \
|
||||||
|
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && SKIP_ENV_VALIDATION=1 pnpm run build; \
|
||||||
|
else echo "Lockfile not found." && exit 1; \
|
||||||
|
fi
|
||||||
|
|
||||||
|
##### RUNNER
|
||||||
|
|
||||||
|
FROM base AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV NODE_ENV production
|
||||||
|
|
||||||
|
# ENV NEXT_TELEMETRY_DISABLED 1
|
||||||
|
|
||||||
|
COPY --from=builder /app/next.config.mjs ./
|
||||||
|
COPY --from=builder /app/public ./public
|
||||||
|
COPY --from=builder /app/package.json ./package.json
|
||||||
|
|
||||||
|
COPY --from=builder /app/.next/standalone ./
|
||||||
|
COPY --from=builder /app/.next/static ./.next/static
|
||||||
|
|
||||||
# start app
|
|
||||||
RUN npm run build
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
CMD npm run start
|
ENV PORT 3000
|
||||||
|
|
||||||
|
CMD ["server.js"]
|
||||||
|
23
README.md
23
README.md
@ -1,33 +1,40 @@
|
|||||||
[](https://app.netlify.com/sites/auditorytraining/deploys)
|
[](https://app.netlify.com/sites/auditorytraining/deploys)
|
||||||
|
|
||||||
# Auditory Training Resources
|
# Auditory Training Resources
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="assets/atr_table.png" width="800" />
|
<img src="assets/atr_table.png" width="800" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
There are many auditory training resources that often have unique requirements. A patient may require an Android application, or help with a specific skill. The patient may also already be at a specific skill level, and want a resource that is more challenging or easier depending on age group. The goal of this application is to provide an accessible website for patients and providers to easily find recommendations for auditory training development resources based on their unique requirements.
|
There are many auditory training resources that often have unique requirements. A patient may require an Android application, or help with a specific skill. The patient may also already be at a specific skill level, and want a resource that is more challenging or easier depending on age group. The goal of this application is to provide an accessible website for patients and providers to easily find recommendations for auditory training development resources based on their unique requirements.
|
||||||
|
|
||||||
## Production Deployment
|
## Production Deployment
|
||||||
|
|
||||||
|
### Docker Method
|
||||||
|
|
||||||
Currently this is being deployed through docker on a single VPS. Using the included docker-compose you can start the production server with the following command:
|
Currently this is being deployed through docker on a single VPS. Using the included docker-compose you can start the production server with the following command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker-compose up --build -d
|
docker-compose up --build -d
|
||||||
```
|
```
|
||||||
|
|
||||||
This should deploy the instance on localhost:1234
|
This should deploy the instance on localhost:1234
|
||||||
|
|
||||||
I have this hosted behind a reverse proxy (nginx) which routes traffic from the auditorytraining.info URI to localhost:1234. You can of course change this to any port that works best in your configuration!
|
I have this hosted behind a reverse proxy (nginx) which routes traffic from the auditorytraining.info URI to localhost:1234. You can of course change this to any port that works best in your configuration!
|
||||||
|
|
||||||
A more permanent hosting solution through the University is in the works.
|
A more permanent hosting solution through the University is in the works.
|
||||||
|
|
||||||
## Local Deployment
|
### Netlify
|
||||||
TODO
|
|
||||||
|
|
||||||
## API Access
|
As of recently, the project has been migrated to netlify enabling us to automate the deployment process and provide a more reliable experience for the end user.
|
||||||
Documentation and updates to our RPC backend API are planned for the future. As of now there is no official documentation for accessing our resources database. More information to come!
|
|
||||||
|
|
||||||
## Directory Breakdown
|
## Directory Breakdown
|
||||||
|
|
||||||
Many directories have associated README's to provide related directory information.
|
Many directories have associated README's to provide related directory information.
|
||||||
|
|
||||||
- ***src/*** - source directory for nextjs frontend/backend application.
|
- **_src/_** - source directory for nextjs frontend/backend application.
|
||||||
- ***data/*** - Non-application (application referring to excelProcessor or frontend) specific data such as the ATR data excel sheet which has multiple uses in each application.
|
- **_data/_** - Non-application (application referring to excelProcessor or frontend) specific data such as the ATR data excel sheet which has multiple uses in each application.
|
||||||
- ***prisma/*** - Prisma schema and seeding of admin account (specified in env).
|
- **_prisma/_** - Prisma schema and seeding of admin account (specified in env).
|
||||||
- ***excelProcessor/*** - Main excel processing backend server and CLI utility for uploading the ATR data excel sheet to MongoDB.
|
- **_excelProcessor/_** - Main excel processing backend server and CLI utility for uploading the ATR data excel sheet to MongoDB.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# Data
|
# Data
|
||||||
|
|
||||||
Backend data is placed in this repository. Currently this consists of the auditory training resources
|
Backend data is placed in this repository. Currently this consists of the auditory training resources
|
||||||
|
|
||||||
Utilize `base_template.xlsx` as your starting point when creating auditory training resource data in excell form. This structure is what the upload script will look for. If your excell sheet is missing any of these required fields the sheet parser will fail and notify you that the upload could not complete.
|
Utilize `base_template.xlsx` as your starting point when creating auditory training resource data in excell form. This structure is what the upload script will look for. If your excell sheet is missing any of these required fields the sheet parser will fail and notify you that the upload could not complete.
|
@ -1,10 +1,16 @@
|
|||||||
version: '3'
|
version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
uiowa-atr:
|
uiowa-atr:
|
||||||
build: .
|
build:
|
||||||
|
context: .
|
||||||
restart: always
|
restart: always
|
||||||
|
pull_policy: build
|
||||||
ports:
|
ports:
|
||||||
- "1234:3000"
|
- "${PORT}:3000"
|
||||||
environment:
|
environment:
|
||||||
NODE_ENV: production
|
- NEXTAUTH_SECRET
|
||||||
|
- NEXTAUTH_URL
|
||||||
|
- DATABASE_URL
|
||||||
|
- JWT_SECRET
|
||||||
|
- NODE_ENV
|
||||||
|
@ -3,27 +3,37 @@
|
|||||||
The purpose of the import script is to take the formatted excel sheet (found in /data/atr_data.xlsx) and upload it to the remote mongoDB server.
|
The purpose of the import script is to take the formatted excel sheet (found in /data/atr_data.xlsx) and upload it to the remote mongoDB server.
|
||||||
|
|
||||||
# Tutorial
|
# Tutorial
|
||||||
|
|
||||||
This script is currently in early stages and was initially designed to simplify uploading the assets until a more permanent web UI could be built for the task. Therefore this utility is very low-level and likely requires some tinkering outside its intended use case.
|
This script is currently in early stages and was initially designed to simplify uploading the assets until a more permanent web UI could be built for the task. Therefore this utility is very low-level and likely requires some tinkering outside its intended use case.
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
1. Open import/main.py
|
1. Open import/main.py
|
||||||
2. Configure the two globals:
|
2. Configure the two globals:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
MONGO_URL = "enter mongo DB URL"
|
MONGO_URL = "enter mongo DB URL"
|
||||||
MONGO_DB = "name of DB"
|
MONGO_DB = "name of DB"
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Make sure dependencies are intalled with `poetry install`
|
3. Make sure dependencies are intalled with `poetry install`
|
||||||
|
|
||||||
## Run
|
## Run
|
||||||
|
|
||||||
The script takes one parameter which is simply the path of your excel sheet.
|
The script takes one parameter which is simply the path of your excel sheet.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
poetry run python import/main.py $PATH
|
poetry run python import/main.py $PATH
|
||||||
```
|
```
|
||||||
|
|
||||||
For example; if uploading the data provided in this repository you can run the following:
|
For example; if uploading the data provided in this repository you can run the following:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
poetry run python import/main.py ../data/atr_data.xlsx
|
poetry run python import/main.py ../data/atr_data.xlsx
|
||||||
```
|
```
|
||||||
|
|
||||||
You should see output showing the individual objects it extracted from your excel sheet along with a success message at the end.
|
You should see output showing the individual objects it extracted from your excel sheet along with a success message at the end.
|
||||||
|
|
||||||
# Expected Data Format
|
# Expected Data Format
|
||||||
|
|
||||||
See {project_root}/data/atr_data.xlsx
|
See {project_root}/data/atr_data.xlsx
|
28
fly.toml
Normal file
28
fly.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# fly.toml app configuration file generated for uiowa-atr on 2024-04-23T16:22:44-05:00
|
||||||
|
#
|
||||||
|
# See https://fly.io/docs/reference/configuration/ for information about how to use this file.
|
||||||
|
#
|
||||||
|
|
||||||
|
app = 'uiowa-atr'
|
||||||
|
primary_region = 'ord'
|
||||||
|
|
||||||
|
[build]
|
||||||
|
|
||||||
|
[http_service]
|
||||||
|
internal_port = 3000
|
||||||
|
force_https = true
|
||||||
|
auto_stop_machines = true
|
||||||
|
auto_start_machines = true
|
||||||
|
min_machines_running = 1
|
||||||
|
processes = ['app']
|
||||||
|
|
||||||
|
[[http_service.checks]]
|
||||||
|
interval = '30s'
|
||||||
|
timeout = '5s'
|
||||||
|
grace_period = '10s'
|
||||||
|
method = 'GET'
|
||||||
|
path = '/api/healthcheck'
|
||||||
|
|
||||||
|
[[vm]]
|
||||||
|
memory = '256MB'
|
||||||
|
size = 'shared-cpu-1x'
|
@ -9,6 +9,7 @@
|
|||||||
/** @type {import("next").NextConfig} */
|
/** @type {import("next").NextConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
|
output: "standalone",
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If you have the "experimental: { appDir: true }" setting enabled, then you
|
* If you have the "experimental: { appDir: true }" setting enabled, then you
|
||||||
|
696
package-lock.json
generated
696
package-lock.json
generated
@ -20,12 +20,13 @@
|
|||||||
"argon2": "^0.30.3",
|
"argon2": "^0.30.3",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"formidable": "^3.5.0",
|
"formidable": "^3.5.0",
|
||||||
"next": "^13.2.1",
|
"next": "^14.1.0",
|
||||||
"next-auth": "^4.19.0",
|
"next-auth": "^4.24.6",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.44.3",
|
"react-hook-form": "^7.44.3",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
|
"sharp": "^0.33.2",
|
||||||
"superjson": "1.9.1",
|
"superjson": "1.9.1",
|
||||||
"zod": "^3.20.6"
|
"zod": "^3.20.6"
|
||||||
},
|
},
|
||||||
@ -108,6 +109,15 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@emnapi/runtime": {
|
||||||
|
"version": "0.45.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-0.45.0.tgz",
|
||||||
|
"integrity": "sha512-Txumi3td7J4A/xTTwlssKieHKTGl3j4A1tglBx72auZ49YK7ePY6XZricgIg9mnZT4xPfA+UPCUdnhRuEFDL+w==",
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@eslint-community/eslint-utils": {
|
"node_modules/@eslint-community/eslint-utils": {
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||||
@ -205,6 +215,437 @@
|
|||||||
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/@img/sharp-darwin-arm64": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-itHBs1rPmsmGF9p4qRe++CzCgd+kFYktnsoR1sbIAfsRMrJZau0Tt1AH9KVnufc2/tU02Gf6Ibujx+15qRE03w==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.26",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-darwin-arm64": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-darwin-x64": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-/rK/69Rrp9x5kaWBjVN07KixZanRr+W1OiyKdXcbjQD6KbW+obaTeBBtLUAtbBsnlTTmWthw99xqoOS7SsySDg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.26",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-darwin-x64": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-kQyrSNd6lmBV7O0BUiyu/OEw9yeNGFbQhbxswS1i6rMDwBBSX+e+rPzu3S+MwAiGU3HdLze3PanQ4Xkfemgzcw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"macos": ">=11",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-eVU/JYLPVjhhrd8Tk6gosl5pVlvsqiFlt50wotCvdkFGf+mDNBJxMh+bvav+Wt3EBnNZWq8Sp2I7XfSjm8siog==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"macos": ">=10.13",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-FtdMvR4R99FTsD53IA3LxYGghQ82t3yt0ZQ93WMZ2xV3dqrb0E8zq4VHaTOuLEAuA83oDawHV3fd+BsAPadHIQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.28",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-bnGG+MJjdX70mAQcSLxgeJco11G+MxTz+ebxlz8Y3dxyeb3Nkl7LgLI0mXupoO+u1wRNx/iRj5yHtzA4sde1yA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.26",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-3+rzfAR1YpMOeA2zZNp+aYEzGNWK4zF3+sdMxuCS3ey9HhDbJ66w6hDSHDMoap32DueFwhhs3vwooAB2MaK4XQ==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.28",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-3NR1mxFsaSgMMzz1bAnnKbSAI+lHXVTqAHgc1bgzjHuXjo4hlscpUxc0vFSAPKI3yuzdzcZOkq7nDPrP2F8Jgw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.26",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-5aBRcjHDG/T6jwC3Edl3lP8nl9U2Yo8+oTl5drd1dh9Z1EBfzUKAJFUDTDisDjUwc7N4AjnPGfCA3jl3hY8uDg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"musl": ">=1.2.2",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-dcT7inI9DBFK6ovfeWRe3hG30h51cBAP5JXlZfx6pzc/Mnf9HFCQDLtYf4MCBjxaaTfjCCjkBxcy3XzOAo5txw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"musl": ">=1.2.2",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-arm": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-Fndk/4Zq3vAc4G/qyfXASbS3HBZbKrlnKZLEJzPLrXoJuipFNNwTes71+Ki1hwYW5lch26niRYoZFAtZVf3EGA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.28",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-arm": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-arm64": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-pz0NNo882vVfqJ0yNInuG9YH71smP4gRSdeL09ukC2YLE6ZyZePAlWKEHgAzJGTiOh8Qkaov6mMIMlEhmLdKew==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.26",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-arm64": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-s390x": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-MBoInDXDppMfhSzbMmOQtGfloVAflS2rP1qPcUIiITMi36Mm5YR7r0ASND99razjQUpHTzjrU1flO76hKvP5RA==",
|
||||||
|
"cpu": [
|
||||||
|
"s390x"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.28",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-s390x": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linux-x64": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-xUT82H5IbXewKkeF5aiooajoO1tQV4PnKfS/OZtb5DDdxS/FCI/uXTVZ35GQ97RZXsycojz/AJ0asoz6p2/H/A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"glibc": ">=2.26",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linux-x64": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-F+0z8JCu/UnMzg8IYW1TMeiViIWBVg7IWP6nE0p5S5EPQxlLd76c8jYemG21X99UzFwgkRo5yz2DS+zbrnxZeA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"musl": ">=1.2.2",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-+ZLE3SQmSL+Fn1gmSaM8uFusW5Y3J9VOf+wMGNnTtJUMUxFhv+P4UPaYEYT8tqnyYVaOVGgMN/zsOxn9pSsO2A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"musl": ">=1.2.2",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64": "1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-wasm32": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-fLbTaESVKuQcpm8ffgBD7jLb/CQLcATju/jxtTXR1XCLwbOQt+OL5zPHSDMmp2JZIeq82e18yE0Vv7zh6+6BfQ==",
|
||||||
|
"cpu": [
|
||||||
|
"wasm32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@emnapi/runtime": "^0.45.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-win32-ia32": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-okBpql96hIGuZ4lN3+nsAjGeggxKm7hIRu9zyec0lnfB8E7Z6p95BuRZzDDXZOl2e8UmR4RhYt631i7mfmKU8g==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@img/sharp-win32-x64": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-E4magOks77DK47FwHUIGH0RYWSgRBfGdK56kIHSVeB9uIS4pPFr4N2kIVsXdQQo4LzOsENKV5KAhRlRL7eMAdg==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0",
|
||||||
|
"npm": ">=9.6.5",
|
||||||
|
"pnpm": ">=7.1.0",
|
||||||
|
"yarn": ">=3.2.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jridgewell/gen-mapping": {
|
"node_modules/@jridgewell/gen-mapping": {
|
||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||||
@ -282,9 +723,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/env": {
|
"node_modules/@next/env": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.0.tgz",
|
||||||
"integrity": "sha512-FsAT5x0jF2kkhNkKkukhsyYOrRqtSxrEhfliniIq0bwWbuXLgyt3Gv0Ml+b91XwjwArmuP7NxCiGd++GGKdNMQ=="
|
"integrity": "sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw=="
|
||||||
},
|
},
|
||||||
"node_modules/@next/eslint-plugin-next": {
|
"node_modules/@next/eslint-plugin-next": {
|
||||||
"version": "13.4.19",
|
"version": "13.4.19",
|
||||||
@ -296,9 +737,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-arm64": {
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.0.tgz",
|
||||||
"integrity": "sha512-vv1qrjXeGbuF2mOkhkdxMDtv9np7W4mcBtaDnHU+yJG+bBwa6rYsYSCI/9Xm5+TuF5SbZbrWO6G1NfTh1TMjvQ==",
|
"integrity": "sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -311,9 +752,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-darwin-x64": {
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.0.tgz",
|
||||||
"integrity": "sha512-jyzO6wwYhx6F+7gD8ddZfuqO4TtpJdw3wyOduR4fxTUCm3aLw7YmHGYNjS0xRSYGAkLpBkH1E0RcelyId6lNsw==",
|
"integrity": "sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -326,9 +767,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.0.tgz",
|
||||||
"integrity": "sha512-vdlnIlaAEh6H+G6HrKZB9c2zJKnpPVKnA6LBwjwT2BTjxI7e0Hx30+FoWCgi50e+YO49p6oPOtesP9mXDRiiUg==",
|
"integrity": "sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -341,9 +782,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-arm64-musl": {
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.0.tgz",
|
||||||
"integrity": "sha512-aU0HkH2XPgxqrbNRBFb3si9Ahu/CpaR5RPmN2s9GiM9qJCiBBlZtRTiEca+DC+xRPyCThTtWYgxjWHgU7ZkyvA==",
|
"integrity": "sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -356,9 +797,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-gnu": {
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.0.tgz",
|
||||||
"integrity": "sha512-htwOEagMa/CXNykFFeAHHvMJeqZfNQEoQvHfsA4wgg5QqGNqD5soeCer4oGlCol6NGUxknrQO6VEustcv+Md+g==",
|
"integrity": "sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -371,9 +812,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-linux-x64-musl": {
|
"node_modules/@next/swc-linux-x64-musl": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.0.tgz",
|
||||||
"integrity": "sha512-4Gj4vvtbK1JH8ApWTT214b3GwUh9EKKQjY41hH/t+u55Knxi/0wesMzwQRhppK6Ddalhu0TEttbiJ+wRcoEj5Q==",
|
"integrity": "sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -386,9 +827,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.0.tgz",
|
||||||
"integrity": "sha512-bUfDevQK4NsIAHXs3/JNgnvEY+LRyneDN788W2NYiRIIzmILjba7LaQTfihuFawZDhRtkYCv3JDC3B4TwnmRJw==",
|
"integrity": "sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@ -401,9 +842,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.0.tgz",
|
||||||
"integrity": "sha512-Y5kikILFAr81LYIFaw6j/NrOtmiM4Sf3GtOc0pn50ez2GCkr+oejYuKGcwAwq3jiTKuzF6OF4iT2INPoxRycEA==",
|
"integrity": "sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@ -416,9 +857,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@next/swc-win32-x64-msvc": {
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.0.tgz",
|
||||||
"integrity": "sha512-YzA78jBDXMYiINdPdJJwGgPNT3YqBNNGhsthsDoWHL9p24tEJn9ViQf/ZqTbwSpX/RrkPupLfuuTH2sf73JBAw==",
|
"integrity": "sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@ -520,9 +961,9 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@swc/helpers": {
|
"node_modules/@swc/helpers": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz",
|
||||||
"integrity": "sha512-sJ902EfIzn1Fa+qYmjdQqh8tPsoxyBz+8yBKC2HKUxyezKJFwPGOn7pv4WY6QuQW//ySQi5lJjA/ZT9sNWWNTg==",
|
"integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"tslib": "^2.4.0"
|
"tslib": "^2.4.0"
|
||||||
}
|
}
|
||||||
@ -1406,9 +1847,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001524",
|
"version": "1.0.30001588",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001588.tgz",
|
||||||
"integrity": "sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA==",
|
"integrity": "sha512-+hVY9jE44uKLkH0SrUTqxjxqNTOWHsbnQDIKjwkZ3lNTzUUVdBLBGXtj/q5Mp5u98r3droaZAewQuEDzjQdZlQ==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -1492,11 +1933,22 @@
|
|||||||
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
|
||||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/color": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1",
|
||||||
|
"color-string": "^1.9.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
},
|
},
|
||||||
@ -1507,8 +1959,16 @@
|
|||||||
"node_modules/color-name": {
|
"node_modules/color-name": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
||||||
"dev": true
|
},
|
||||||
|
"node_modules/color-string": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
|
||||||
|
"dependencies": {
|
||||||
|
"color-name": "^1.0.0",
|
||||||
|
"simple-swizzle": "^0.2.2"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/color-support": {
|
"node_modules/color-support": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
@ -2690,11 +3150,6 @@
|
|||||||
"node": ">=10.13.0"
|
"node": ">=10.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/glob-to-regexp": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="
|
|
||||||
},
|
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "13.21.0",
|
"version": "13.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz",
|
||||||
@ -2965,6 +3420,11 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-arrayish": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
|
||||||
|
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="
|
||||||
|
},
|
||||||
"node_modules/is-async-function": {
|
"node_modules/is-async-function": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz",
|
||||||
@ -3653,35 +4113,34 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/next": {
|
"node_modules/next": {
|
||||||
"version": "13.4.19",
|
"version": "14.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/next/-/next-13.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/next/-/next-14.1.0.tgz",
|
||||||
"integrity": "sha512-HuPSzzAbJ1T4BD8e0bs6B9C1kWQ6gv8ykZoRWs5AQoiIuqbGHHdQO7Ljuvg05Q0Z24E2ABozHe6FxDvI6HfyAw==",
|
"integrity": "sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@next/env": "13.4.19",
|
"@next/env": "14.1.0",
|
||||||
"@swc/helpers": "0.5.1",
|
"@swc/helpers": "0.5.2",
|
||||||
"busboy": "1.6.0",
|
"busboy": "1.6.0",
|
||||||
"caniuse-lite": "^1.0.30001406",
|
"caniuse-lite": "^1.0.30001579",
|
||||||
"postcss": "8.4.14",
|
"graceful-fs": "^4.2.11",
|
||||||
"styled-jsx": "5.1.1",
|
"postcss": "8.4.31",
|
||||||
"watchpack": "2.4.0",
|
"styled-jsx": "5.1.1"
|
||||||
"zod": "3.21.4"
|
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"next": "dist/bin/next"
|
"next": "dist/bin/next"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.8.0"
|
"node": ">=18.17.0"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@next/swc-darwin-arm64": "13.4.19",
|
"@next/swc-darwin-arm64": "14.1.0",
|
||||||
"@next/swc-darwin-x64": "13.4.19",
|
"@next/swc-darwin-x64": "14.1.0",
|
||||||
"@next/swc-linux-arm64-gnu": "13.4.19",
|
"@next/swc-linux-arm64-gnu": "14.1.0",
|
||||||
"@next/swc-linux-arm64-musl": "13.4.19",
|
"@next/swc-linux-arm64-musl": "14.1.0",
|
||||||
"@next/swc-linux-x64-gnu": "13.4.19",
|
"@next/swc-linux-x64-gnu": "14.1.0",
|
||||||
"@next/swc-linux-x64-musl": "13.4.19",
|
"@next/swc-linux-x64-musl": "14.1.0",
|
||||||
"@next/swc-win32-arm64-msvc": "13.4.19",
|
"@next/swc-win32-arm64-msvc": "14.1.0",
|
||||||
"@next/swc-win32-ia32-msvc": "13.4.19",
|
"@next/swc-win32-ia32-msvc": "14.1.0",
|
||||||
"@next/swc-win32-x64-msvc": "13.4.19"
|
"@next/swc-win32-x64-msvc": "14.1.0"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@opentelemetry/api": "^1.1.0",
|
"@opentelemetry/api": "^1.1.0",
|
||||||
@ -3699,9 +4158,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/next-auth": {
|
"node_modules/next-auth": {
|
||||||
"version": "4.23.1",
|
"version": "4.24.6",
|
||||||
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.23.1.tgz",
|
"resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.6.tgz",
|
||||||
"integrity": "sha512-mL083z8KgRtlrIV6CDca2H1kduWJuK/3pTS0Fe2og15KOm4v2kkLGdSDfc2g+019aEBrJUT0pPW2Xx42ImN1WA==",
|
"integrity": "sha512-djQt3ZEaWEIxcsuh3HTW2uuzLfXMRjHH+ugAsichlQSbH4iA5MRcgMA2HvTNvsDTDLh44tyU72+/gWsxgTbAKg==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.20.13",
|
"@babel/runtime": "^7.20.13",
|
||||||
"@panva/hkdf": "^1.0.2",
|
"@panva/hkdf": "^1.0.2",
|
||||||
@ -3714,7 +4173,7 @@
|
|||||||
"uuid": "^8.3.2"
|
"uuid": "^8.3.2"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"next": "^12.2.5 || ^13",
|
"next": "^12.2.5 || ^13 || ^14",
|
||||||
"nodemailer": "^6.6.5",
|
"nodemailer": "^6.6.5",
|
||||||
"react": "^17.0.2 || ^18",
|
"react": "^17.0.2 || ^18",
|
||||||
"react-dom": "^17.0.2 || ^18"
|
"react-dom": "^17.0.2 || ^18"
|
||||||
@ -3725,37 +4184,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/next/node_modules/postcss": {
|
|
||||||
"version": "8.4.14",
|
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
|
|
||||||
"integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/postcss/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tidelift",
|
|
||||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"nanoid": "^3.3.4",
|
|
||||||
"picocolors": "^1.0.0",
|
|
||||||
"source-map-js": "^1.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || >=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/next/node_modules/zod": {
|
|
||||||
"version": "3.21.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.21.4.tgz",
|
|
||||||
"integrity": "sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==",
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/colinhacks"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-addon-api": {
|
"node_modules/node-addon-api": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||||
@ -4125,10 +4553,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.28",
|
"version": "8.4.31",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.28.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
|
||||||
"integrity": "sha512-Z7V5j0cq8oEKyejIKfpD8b4eBy9cwW2JWPk0+fB1HOAMsfHbnAXLLS+PfVWlzMSLQaWttKDt607I0XHmpE67Vw==",
|
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@ -4752,6 +5179,45 @@
|
|||||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/sharp": {
|
||||||
|
"version": "0.33.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.2.tgz",
|
||||||
|
"integrity": "sha512-WlYOPyyPDiiM07j/UO+E720ju6gtNtHjEGg5vovUk1Lgxyjm2LFO+37Nt/UI3MMh2l6hxTWQWi7qk3cXJTutcQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"color": "^4.2.3",
|
||||||
|
"detect-libc": "^2.0.2",
|
||||||
|
"semver": "^7.5.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"libvips": ">=8.15.1",
|
||||||
|
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://opencollective.com/libvips"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@img/sharp-darwin-arm64": "0.33.2",
|
||||||
|
"@img/sharp-darwin-x64": "0.33.2",
|
||||||
|
"@img/sharp-libvips-darwin-arm64": "1.0.1",
|
||||||
|
"@img/sharp-libvips-darwin-x64": "1.0.1",
|
||||||
|
"@img/sharp-libvips-linux-arm": "1.0.1",
|
||||||
|
"@img/sharp-libvips-linux-arm64": "1.0.1",
|
||||||
|
"@img/sharp-libvips-linux-s390x": "1.0.1",
|
||||||
|
"@img/sharp-libvips-linux-x64": "1.0.1",
|
||||||
|
"@img/sharp-libvips-linuxmusl-arm64": "1.0.1",
|
||||||
|
"@img/sharp-libvips-linuxmusl-x64": "1.0.1",
|
||||||
|
"@img/sharp-linux-arm": "0.33.2",
|
||||||
|
"@img/sharp-linux-arm64": "0.33.2",
|
||||||
|
"@img/sharp-linux-s390x": "0.33.2",
|
||||||
|
"@img/sharp-linux-x64": "0.33.2",
|
||||||
|
"@img/sharp-linuxmusl-arm64": "0.33.2",
|
||||||
|
"@img/sharp-linuxmusl-x64": "0.33.2",
|
||||||
|
"@img/sharp-wasm32": "0.33.2",
|
||||||
|
"@img/sharp-win32-ia32": "0.33.2",
|
||||||
|
"@img/sharp-win32-x64": "0.33.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/shebang-command": {
|
"node_modules/shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
@ -4792,6 +5258,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/simple-swizzle": {
|
||||||
|
"version": "0.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
|
||||||
|
"integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
|
||||||
|
"dependencies": {
|
||||||
|
"is-arrayish": "^0.3.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/slash": {
|
"node_modules/slash": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||||
@ -5437,18 +5911,6 @@
|
|||||||
"loose-envify": "^1.0.0"
|
"loose-envify": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/watchpack": {
|
|
||||||
"version": "2.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
|
||||||
"integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"glob-to-regexp": "^0.4.1",
|
|
||||||
"graceful-fs": "^4.1.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.13.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/webidl-conversions": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
@ -27,12 +27,13 @@
|
|||||||
"argon2": "^0.30.3",
|
"argon2": "^0.30.3",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"formidable": "^3.5.0",
|
"formidable": "^3.5.0",
|
||||||
"next": "^13.2.1",
|
"next": "^14.1.0",
|
||||||
"next-auth": "^4.19.0",
|
"next-auth": "^4.24.6",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"react-hook-form": "^7.44.3",
|
"react-hook-form": "^7.44.3",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
|
"sharp": "^0.33.2",
|
||||||
"superjson": "1.9.1",
|
"superjson": "1.9.1",
|
||||||
"zod": "^3.20.6"
|
"zod": "^3.20.6"
|
||||||
},
|
},
|
||||||
|
@ -70,7 +70,7 @@ type Photo {
|
|||||||
|
|
||||||
model AuditoryResource {
|
model AuditoryResource {
|
||||||
id String @id @default(auto()) @map("_id") @db.ObjectId
|
id String @id @default(auto()) @map("_id") @db.ObjectId
|
||||||
icon String
|
icon String?
|
||||||
name String
|
name String
|
||||||
description String
|
description String
|
||||||
photo Photo?
|
photo Photo?
|
||||||
|
@ -8,7 +8,7 @@ type ResourcePhotoProps = (
|
|||||||
src: string | undefined;
|
src: string | undefined;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
src: string;
|
src: string | undefined;
|
||||||
photo: null;
|
photo: null;
|
||||||
}
|
}
|
||||||
) & { name: string };
|
) & { name: string };
|
||||||
|
@ -49,7 +49,7 @@ export const ResourceInfo = ({
|
|||||||
<ResourcePhoto
|
<ResourcePhoto
|
||||||
name={resource.name}
|
name={resource.name}
|
||||||
photo={resource.photo}
|
photo={resource.photo}
|
||||||
src={resource.icon}
|
src={resource.icon ?? undefined}
|
||||||
/>
|
/>
|
||||||
<span className="block rounded-lg border border-neutral-900 bg-neutral-900 py-[1px] text-center text-white hover:bg-neutral-500 print:hidden">
|
<span className="block rounded-lg border border-neutral-900 bg-neutral-900 py-[1px] text-center text-white hover:bg-neutral-500 print:hidden">
|
||||||
more info
|
more info
|
||||||
@ -61,7 +61,7 @@ export const ResourceInfo = ({
|
|||||||
<ResourcePhoto
|
<ResourcePhoto
|
||||||
name={resource.name}
|
name={resource.name}
|
||||||
photo={resource.photo}
|
photo={resource.photo}
|
||||||
src={resource.icon}
|
src={resource.icon ?? undefined}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { ExclamationCircleIcon } from "@heroicons/react/24/solid";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import { type ButtonHTMLAttributes, useEffect, useState } from "react";
|
||||||
|
|
||||||
const AdminActionBody = ({
|
const AdminActionBody = ({
|
||||||
label,
|
label,
|
||||||
@ -38,17 +40,67 @@ const AdminActionLink = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AdminActionConfirmButton = ({
|
||||||
|
label,
|
||||||
|
onConfirm,
|
||||||
|
symbol,
|
||||||
|
type = "button",
|
||||||
|
}: {
|
||||||
|
label: string;
|
||||||
|
onConfirm?: () => void;
|
||||||
|
symbol: JSX.Element | undefined;
|
||||||
|
type?: ButtonHTMLAttributes<HTMLButtonElement>["type"];
|
||||||
|
}) => {
|
||||||
|
const [isConfirmView, setConfirmView] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isConfirmView) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (isConfirmView) {
|
||||||
|
setConfirmView(false);
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
}, [isConfirmView]);
|
||||||
|
|
||||||
|
if (isConfirmView) {
|
||||||
|
return (
|
||||||
|
<AdminActionButton
|
||||||
|
symbol={<ExclamationCircleIcon className="w-4 animate-ping" />}
|
||||||
|
label={`Confirm ${label}`}
|
||||||
|
onClick={onConfirm}
|
||||||
|
type={type}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AdminActionButton
|
||||||
|
symbol={symbol}
|
||||||
|
label={label}
|
||||||
|
onClick={() => {
|
||||||
|
setConfirmView(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const AdminActionButton = ({
|
const AdminActionButton = ({
|
||||||
label,
|
label,
|
||||||
onClick,
|
onClick,
|
||||||
symbol,
|
symbol,
|
||||||
|
type = "button",
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label: string;
|
||||||
onClick: () => void;
|
onClick?: () => void;
|
||||||
symbol: JSX.Element | undefined;
|
symbol: JSX.Element | undefined;
|
||||||
|
type?: ButtonHTMLAttributes<HTMLButtonElement>["type"];
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
type={type}
|
||||||
className="py-auto group my-auto h-full space-x-2 rounded-lg border border-neutral-400 bg-neutral-800 px-2 hover:border-neutral-800 hover:bg-white"
|
className="py-auto group my-auto h-full space-x-2 rounded-lg border border-neutral-400 bg-neutral-800 px-2 hover:border-neutral-800 hover:bg-white"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
>
|
>
|
||||||
@ -62,4 +114,4 @@ const AdminActionButton = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export { AdminActionLink, AdminActionButton };
|
export { AdminActionLink, AdminActionButton, AdminActionConfirmButton };
|
||||||
|
@ -41,12 +41,14 @@ import Modal from "react-modal";
|
|||||||
import { type RouterInputs } from "~/utils/api";
|
import { type RouterInputs } from "~/utils/api";
|
||||||
import { PlatformLinkButton } from "~/pages/resources/[id]";
|
import { PlatformLinkButton } from "~/pages/resources/[id]";
|
||||||
import { ResourcePhoto } from "~/components/ResourcePhoto";
|
import { ResourcePhoto } from "~/components/ResourcePhoto";
|
||||||
|
import { FieldLabel } from "~/components/forms/inputLabel";
|
||||||
|
|
||||||
// Required for accessibility
|
// Required for accessibility
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
||||||
Modal.setAppElement("#__next");
|
Modal.setAppElement("#__next");
|
||||||
|
|
||||||
export type ResourceUpdateInput = RouterInputs["auditoryResource"]["update"];
|
export type ResourceUpdateInput = RouterInputs["auditoryResource"]["update"];
|
||||||
|
export type ResourceCreateInput = RouterInputs["auditoryResource"]["create"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the image selector for resource form.
|
* Renders the image selector for resource form.
|
||||||
@ -86,15 +88,22 @@ const SelectImageInput = () => {
|
|||||||
htmlFor="resource-image-file"
|
htmlFor="resource-image-file"
|
||||||
className="bg-whit group relative cursor-pointer overflow-hidden rounded-xl border border-neutral-400 drop-shadow-lg"
|
className="bg-whit group relative cursor-pointer overflow-hidden rounded-xl border border-neutral-400 drop-shadow-lg"
|
||||||
>
|
>
|
||||||
|
{photo ? (
|
||||||
|
<>
|
||||||
<ResourcePhoto
|
<ResourcePhoto
|
||||||
name={name ?? "unknown resource logo"}
|
name={name ?? "unknown resource logo"}
|
||||||
photo={photo ?? null}
|
photo={photo}
|
||||||
src={icon}
|
src={icon ?? undefined}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="absolute bottom-0 left-0 right-0 top-0 hidden place-items-center group-hover:grid group-hover:bg-white/70">
|
<div className="absolute bottom-0 left-0 right-0 top-0 hidden place-items-center group-hover:grid group-hover:bg-white/70">
|
||||||
<PencilSquareIcon className="w-16 text-black/50" />
|
<PencilSquareIcon className="w-16 text-black/50" />
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="grid aspect-square place-items-center hover:bg-white/70">
|
||||||
|
<PencilSquareIcon className="h-16 w-16 text-black/50" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
@ -259,15 +268,15 @@ const ResourceLinkSubForm = () => {
|
|||||||
<h1 className="text-xl">Links</h1>
|
<h1 className="text-xl">Links</h1>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-6 rounded-full border border-neutral-900 bg-neutral-200 px-2 leading-tight hover:bg-yellow-400"
|
className="flex h-6 flex-row items-center rounded-full border border-neutral-900 bg-neutral-200 px-2 leading-tight drop-shadow-sm hover:bg-yellow-400"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setLinkModalOpen(!linkModalOpen);
|
setLinkModalOpen(!linkModalOpen);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<span className="my-auto inline-block align-middle text-sm font-normal text-neutral-700">
|
<span className="text-sm font-normal leading-3 text-neutral-700">
|
||||||
Add
|
Add
|
||||||
</span>
|
</span>
|
||||||
<PlusIcon className="my-auto inline-block w-4 align-middle" />
|
<PlusIcon className="w-4 leading-3" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -352,16 +361,18 @@ function ResourceSummarySubForm({
|
|||||||
</h2>
|
</h2>
|
||||||
<span className="text-md">
|
<span className="text-md">
|
||||||
<InfoInputLine
|
<InfoInputLine
|
||||||
details={register("manufacturer.name", { required: true })}
|
details={register("manufacturer.name", {
|
||||||
|
required: "Field required",
|
||||||
|
})}
|
||||||
placeholder="manufacturer"
|
placeholder="manufacturer"
|
||||||
value={resource?.manufacturer?.name ?? ""}
|
|
||||||
hint="manufacturer"
|
hint="manufacturer"
|
||||||
|
value={resource?.name}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<InfoInputLine
|
<InfoInputLine
|
||||||
details={register("name", { required: true })}
|
details={register("name", { required: "Field required" })}
|
||||||
placeholder="name"
|
placeholder="name"
|
||||||
value={resource?.name ?? ""}
|
value={resource?.name}
|
||||||
hint="name"
|
hint="name"
|
||||||
/>
|
/>
|
||||||
<span className="my-1 block w-full text-center text-xs italic text-neutral-400">
|
<span className="my-1 block w-full text-center text-xs italic text-neutral-400">
|
||||||
@ -369,12 +380,37 @@ function ResourceSummarySubForm({
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<FieldLabel
|
||||||
|
heading="Age Range"
|
||||||
|
subheading="Specify the minimum and maximum age range supported by the resource"
|
||||||
|
/>
|
||||||
|
<div className="mt-4 flex flex-row space-x-4">
|
||||||
|
<GenericInput
|
||||||
|
type="number"
|
||||||
|
placeholder="minimum age"
|
||||||
|
details={register("ages.min", {
|
||||||
|
required: "Field required",
|
||||||
|
valueAsNumber: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
<span className="text-xl">-</span>
|
||||||
|
<GenericInput
|
||||||
|
type="number"
|
||||||
|
placeholder="maximum age"
|
||||||
|
details={register("ages.max", {
|
||||||
|
required: "Field required",
|
||||||
|
valueAsNumber: true,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<MultiSelectorMany
|
<MultiSelectorMany
|
||||||
details={register("payment_options", { required: true })}
|
details={register("payment_options", { required: "Field required" })}
|
||||||
label="Price Category"
|
label="Price Category"
|
||||||
defaultValues={
|
defaultValues={resource?.payment_options ?? []}
|
||||||
resource?.payment_options ?? [PaymentType.FREE.toString()]
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<PaymentTypeOption type={PaymentType.FREE} label="Free" />
|
<PaymentTypeOption type={PaymentType.FREE} label="Free" />
|
||||||
<PaymentTypeOption
|
<PaymentTypeOption
|
||||||
@ -388,7 +424,7 @@ function ResourceSummarySubForm({
|
|||||||
</MultiSelectorMany>
|
</MultiSelectorMany>
|
||||||
|
|
||||||
<MultiSelectorMany
|
<MultiSelectorMany
|
||||||
details={register("skill_levels", { required: true })}
|
details={register("skill_levels", { required: "Field required" })}
|
||||||
label="Skill Level"
|
label="Skill Level"
|
||||||
defaultValues={resource?.skill_levels ?? []}
|
defaultValues={resource?.skill_levels ?? []}
|
||||||
>
|
>
|
||||||
@ -404,7 +440,7 @@ function ResourceSummarySubForm({
|
|||||||
</MultiSelectorMany>
|
</MultiSelectorMany>
|
||||||
|
|
||||||
<MultiSelectorMany
|
<MultiSelectorMany
|
||||||
details={register("skills", { required: true })}
|
details={register("skills", { required: "Field required" })}
|
||||||
label="Skills Covered"
|
label="Skills Covered"
|
||||||
defaultValues={resource?.skills ?? []}
|
defaultValues={resource?.skills ?? []}
|
||||||
>
|
>
|
||||||
@ -446,7 +482,7 @@ const ResourceDescriptionSubForm = () => {
|
|||||||
<ChevronDownIcon className="mx-2 my-auto w-4 text-white group-hover:animate-bounce" />
|
<ChevronDownIcon className="mx-2 my-auto w-4 text-white group-hover:animate-bounce" />
|
||||||
</button>
|
</button>
|
||||||
<textarea
|
<textarea
|
||||||
{...register("description", { required: true })}
|
{...register("description", { required: "Field required" })}
|
||||||
className={
|
className={
|
||||||
"h-48 w-full rounded-b-xl p-2" + (dropdownOpen ? " hidden" : "")
|
"h-48 w-full rounded-b-xl p-2" + (dropdownOpen ? " hidden" : "")
|
||||||
}
|
}
|
||||||
|
16
src/components/forms/inputLabel.tsx
Normal file
16
src/components/forms/inputLabel.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export const FieldLabel = ({
|
||||||
|
heading,
|
||||||
|
subheading,
|
||||||
|
}: {
|
||||||
|
heading: string;
|
||||||
|
subheading: string;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<label className="text-md block font-semibold">{heading}</label>
|
||||||
|
<span className="block text-sm italic text-neutral-400">
|
||||||
|
{subheading}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -4,6 +4,7 @@ import {
|
|||||||
useFormContext,
|
useFormContext,
|
||||||
type UseFormRegisterReturn,
|
type UseFormRegisterReturn,
|
||||||
} from "react-hook-form";
|
} from "react-hook-form";
|
||||||
|
import { FieldLabel } from "./inputLabel";
|
||||||
|
|
||||||
// generics
|
// generics
|
||||||
interface ToStringable {
|
interface ToStringable {
|
||||||
@ -60,10 +61,7 @@ function MultiSelectorMany<T extends ToStringable>({
|
|||||||
<SelectorContext.Provider value={{ type: "many", updateCallback }}>
|
<SelectorContext.Provider value={{ type: "many", updateCallback }}>
|
||||||
<SelectedManyContext.Provider value={selected}>
|
<SelectedManyContext.Provider value={selected}>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<label className="text-md block font-semibold">{label}</label>
|
<FieldLabel heading={label} subheading="Select all that apply" />
|
||||||
<span className="block text-sm italic text-neutral-400">
|
|
||||||
Select all that apply
|
|
||||||
</span>
|
|
||||||
<input {...details} readOnly type="text" className="hidden" />
|
<input {...details} readOnly type="text" className="hidden" />
|
||||||
<div className="mt-2 space-x-2 space-y-2 overflow-x-auto">
|
<div className="mt-2 space-x-2 space-y-2 overflow-x-auto">
|
||||||
{children}
|
{children}
|
||||||
@ -100,10 +98,7 @@ function MultiSelector<T extends ToStringable>({
|
|||||||
>
|
>
|
||||||
<SelectedUniqueContext.Provider value={selected}>
|
<SelectedUniqueContext.Provider value={selected}>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<label className="text-md block font-semibold">{label}</label>
|
<FieldLabel heading={label} subheading="Select one from below" />
|
||||||
<span className="block text-sm italic text-neutral-400">
|
|
||||||
Select one from below
|
|
||||||
</span>
|
|
||||||
<input {...details} readOnly type="text" className="hidden" />
|
<input {...details} readOnly type="text" className="hidden" />
|
||||||
<div className="space-x-2 space-y-2 overflow-x-auto">{children}</div>
|
<div className="space-x-2 space-y-2 overflow-x-auto">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,7 @@ function InfoInputLine<TFieldName extends InternalFieldName>({
|
|||||||
hint,
|
hint,
|
||||||
details,
|
details,
|
||||||
}: {
|
}: {
|
||||||
value: string;
|
value?: string | undefined;
|
||||||
placeholder: string;
|
placeholder: string;
|
||||||
hint?: string;
|
hint?: string;
|
||||||
details: UseFormRegisterReturn<TFieldName>;
|
details: UseFormRegisterReturn<TFieldName>;
|
||||||
@ -50,16 +50,18 @@ function GenericInput<TFieldName extends InternalFieldName>({
|
|||||||
type = "text",
|
type = "text",
|
||||||
details,
|
details,
|
||||||
}: {
|
}: {
|
||||||
label: string;
|
label?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
type: HTMLInputTypeAttribute;
|
type: HTMLInputTypeAttribute;
|
||||||
details: UseFormRegisterReturn<TFieldName>;
|
details: UseFormRegisterReturn<TFieldName>;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section className="w-full space-y-1">
|
<section className="w-full space-y-1">
|
||||||
|
{label ? (
|
||||||
<label className="text-md block px-1 font-semibold text-neutral-600">
|
<label className="text-md block px-1 font-semibold text-neutral-600">
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
|
) : undefined}
|
||||||
<input
|
<input
|
||||||
className="block h-8 w-full rounded-lg border border-neutral-600 px-2 py-1"
|
className="block h-8 w-full rounded-lg border border-neutral-600 px-2 py-1"
|
||||||
{...details}
|
{...details}
|
||||||
|
@ -27,6 +27,7 @@ const server = z.object({
|
|||||||
*/
|
*/
|
||||||
const client = z.object({
|
const client = z.object({
|
||||||
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1),
|
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1),
|
||||||
|
NEXT_PUBLIC_ENVIRONMENT: z.enum(["development", "test", "production"]),
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,6 +42,7 @@ const processEnv = {
|
|||||||
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
|
||||||
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
|
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
|
||||||
JWT_SECRET: process.env.JWT_SECRET,
|
JWT_SECRET: process.env.JWT_SECRET,
|
||||||
|
NEXT_PUBLIC_ENVIRONMENT: process.env.NODE_ENV,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Don't touch the part below
|
// Don't touch the part below
|
||||||
|
@ -6,6 +6,7 @@ import { api } from "~/utils/api";
|
|||||||
|
|
||||||
import "~/styles/globals.css";
|
import "~/styles/globals.css";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import { env } from "~/env.mjs";
|
||||||
|
|
||||||
const MyApp: AppType<{ session: Session | null }> = ({
|
const MyApp: AppType<{ session: Session | null }> = ({
|
||||||
Component,
|
Component,
|
||||||
@ -20,6 +21,15 @@ const MyApp: AppType<{ session: Session | null }> = ({
|
|||||||
content="University of Iowa Center for Auditory Training Resources"
|
content="University of Iowa Center for Auditory Training Resources"
|
||||||
/>
|
/>
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
{env.NEXT_PUBLIC_ENVIRONMENT === "production" ? (
|
||||||
|
<>
|
||||||
|
<script
|
||||||
|
defer
|
||||||
|
data-domain="auditorytraining.info"
|
||||||
|
src="https://analytics.brandonegger.com/js/script.js"
|
||||||
|
></script>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
</Head>
|
</Head>
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
</SessionProvider>
|
</SessionProvider>
|
||||||
|
12
src/pages/api/healthcheck.ts
Normal file
12
src/pages/api/healthcheck.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
type ResponseData = {
|
||||||
|
status: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function handler(
|
||||||
|
_: NextApiRequest,
|
||||||
|
res: NextApiResponse<ResponseData>
|
||||||
|
) {
|
||||||
|
res.status(200).json({ status: "ok" });
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
import { XCircleIcon } from "@heroicons/react/20/solid";
|
import { XCircleIcon } from "@heroicons/react/20/solid";
|
||||||
import { AdminBarLayout } from "~/components/admin/ControlBar";
|
import { AdminBarLayout } from "~/components/admin/ControlBar";
|
||||||
import { AdminActionButton, AdminActionLink } from "~/components/admin/common";
|
import {
|
||||||
|
AdminActionButton,
|
||||||
|
AdminActionConfirmButton,
|
||||||
|
} from "~/components/admin/common";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import {
|
import {
|
||||||
ResourceForm,
|
ResourceForm,
|
||||||
@ -13,6 +16,7 @@ import { useRouter } from "next/router";
|
|||||||
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
|
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
|
||||||
import { QueryWaitWrapper } from "~/components/LoadingWrapper";
|
import { QueryWaitWrapper } from "~/components/LoadingWrapper";
|
||||||
import { type AuditoryResource } from "@prisma/client";
|
import { type AuditoryResource } from "@prisma/client";
|
||||||
|
import { parseTRPCErrorMessage } from "~/utils/parseTRPCError";
|
||||||
|
|
||||||
const EditResourcePage = () => {
|
const EditResourcePage = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -25,6 +29,9 @@ const EditResourcePage = () => {
|
|||||||
retry(_failureCount, error) {
|
retry(_failureCount, error) {
|
||||||
return error.data?.httpStatus !== 404;
|
return error.data?.httpStatus !== 404;
|
||||||
},
|
},
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
refetchOnMount: false,
|
||||||
|
refetchInterval: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -37,8 +44,8 @@ const EditResourcePage = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { mutate } = api.auditoryResource.update.useMutation({
|
const { mutate } = api.auditoryResource.update.useMutation({
|
||||||
onSuccess: async (_resData) => {
|
onSuccess: async (resData) => {
|
||||||
if (!data) {
|
if (!resData) {
|
||||||
setServerError("An unexpected error has occured");
|
setServerError("An unexpected error has occured");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -46,7 +53,9 @@ const EditResourcePage = () => {
|
|||||||
setServerError(undefined);
|
setServerError(undefined);
|
||||||
await router.push(`/resources/${data.id}`);
|
await router.push(`/resources/${data.id}`);
|
||||||
},
|
},
|
||||||
onError: (error) => setServerError(error.message),
|
onError: (error) => {
|
||||||
|
setServerError(parseTRPCErrorMessage(error.message));
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSubmit: SubmitHandler<ResourceUpdateInput> = (data) => {
|
const onSubmit: SubmitHandler<ResourceUpdateInput> = (data) => {
|
||||||
@ -81,11 +90,15 @@ const EditResourcePage = () => {
|
|||||||
onSubmit(formMethods.getValues());
|
onSubmit(formMethods.getValues());
|
||||||
}}
|
}}
|
||||||
/>,
|
/>,
|
||||||
<AdminActionLink
|
<AdminActionConfirmButton
|
||||||
key="cancel"
|
key="cancel"
|
||||||
symbol={<XCircleIcon className="w-4" />}
|
symbol={<XCircleIcon className="w-4" />}
|
||||||
label="Cancel"
|
label="Cancel"
|
||||||
href={`/resources/${data.id}`}
|
onConfirm={() => {
|
||||||
|
router.push(`/resources/${data.id}`).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>,
|
/>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
@ -6,10 +6,14 @@ import { type AuditoryResource, type PlatformLink } from "@prisma/client";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { AdminBarLayout } from "~/components/admin/ControlBar";
|
import { AdminBarLayout } from "~/components/admin/ControlBar";
|
||||||
import { AdminActionLink } from "~/components/admin/common";
|
import {
|
||||||
|
AdminActionConfirmButton,
|
||||||
|
AdminActionLink,
|
||||||
|
} from "~/components/admin/common";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
|
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
|
||||||
import { QueryWaitWrapper } from "~/components/LoadingWrapper";
|
import { QueryWaitWrapper } from "~/components/LoadingWrapper";
|
||||||
|
import { TrashIcon } from "@heroicons/react/24/outline";
|
||||||
|
|
||||||
export const PlatformLinkButton = ({
|
export const PlatformLinkButton = ({
|
||||||
platformLink,
|
platformLink,
|
||||||
@ -91,6 +95,15 @@ const ResourceViewPage = () => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { mutate: mutateDelete } = api.auditoryResource.delete.useMutation({
|
||||||
|
onSuccess: async () => {
|
||||||
|
await router.push(`/resources`);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
console.error(error);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const ConditionalView = (data: AuditoryResource) => {
|
const ConditionalView = (data: AuditoryResource) => {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto flex max-w-2xl flex-col flex-col-reverse divide-x py-4 sm:flex-row">
|
<div className="mx-auto flex max-w-2xl flex-col flex-col-reverse divide-x py-4 sm:flex-row">
|
||||||
@ -122,13 +135,24 @@ const ResourceViewPage = () => {
|
|||||||
return (
|
return (
|
||||||
<HeaderFooterLayout>
|
<HeaderFooterLayout>
|
||||||
<AdminBarLayout
|
<AdminBarLayout
|
||||||
actions={
|
actions={[
|
||||||
<AdminActionLink
|
<AdminActionLink
|
||||||
|
key="edit"
|
||||||
symbol={<PencilSquareIcon className="w-4" />}
|
symbol={<PencilSquareIcon className="w-4" />}
|
||||||
label="Edit Page"
|
label="Edit Page"
|
||||||
href={`${router.asPath}/edit`}
|
href={`${router.asPath}/edit`}
|
||||||
/>
|
/>,
|
||||||
}
|
<AdminActionConfirmButton
|
||||||
|
key="delete"
|
||||||
|
label="Delete"
|
||||||
|
symbol={<TrashIcon className="w-4" />}
|
||||||
|
onConfirm={() => {
|
||||||
|
mutateDelete({
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<div className="mb-12">
|
<div className="mb-12">
|
||||||
<QueryWaitWrapper query={resourceQuery} Render={ConditionalView} />
|
<QueryWaitWrapper query={resourceQuery} Render={ConditionalView} />
|
||||||
|
86
src/pages/resources/create.tsx
Normal file
86
src/pages/resources/create.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { XCircleIcon, PlusCircleIcon } from "@heroicons/react/20/solid";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
type SubmitHandler,
|
||||||
|
useForm,
|
||||||
|
type UseFormReturn,
|
||||||
|
} from "react-hook-form";
|
||||||
|
import { AdminBarLayout } from "~/components/admin/ControlBar";
|
||||||
|
import {
|
||||||
|
AdminActionButton,
|
||||||
|
AdminActionConfirmButton,
|
||||||
|
} from "~/components/admin/common";
|
||||||
|
import {
|
||||||
|
type ResourceCreateInput,
|
||||||
|
ResourceForm,
|
||||||
|
type ResourceUpdateInput,
|
||||||
|
} from "~/components/admin/resources/form";
|
||||||
|
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
|
||||||
|
import { api } from "~/utils/api";
|
||||||
|
import { parseTRPCErrorMessage } from "~/utils/parseTRPCError";
|
||||||
|
|
||||||
|
const EditResourcePage = () => {
|
||||||
|
const router = useRouter();
|
||||||
|
const formMethods = useForm<ResourceCreateInput>();
|
||||||
|
|
||||||
|
const [serverError, setServerError] = useState<string | undefined>(undefined);
|
||||||
|
|
||||||
|
const { mutate } = api.auditoryResource.create.useMutation({
|
||||||
|
onSuccess: async (resData) => {
|
||||||
|
if (!resData) {
|
||||||
|
setServerError("An unexpected error has occured");
|
||||||
|
}
|
||||||
|
|
||||||
|
setServerError(undefined);
|
||||||
|
await router.push(`/resources/${resData.id}`);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
setServerError(parseTRPCErrorMessage(error.message));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit: SubmitHandler<ResourceCreateInput> = (data) => {
|
||||||
|
mutate(data);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<HeaderFooterLayout>
|
||||||
|
<AdminBarLayout
|
||||||
|
actions={[
|
||||||
|
<AdminActionButton
|
||||||
|
key="create"
|
||||||
|
symbol={<PlusCircleIcon className="w-4" />}
|
||||||
|
label="Create"
|
||||||
|
onClick={() => {
|
||||||
|
formMethods
|
||||||
|
.handleSubmit(onSubmit)()
|
||||||
|
.catch((error) => console.error(error));
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
<AdminActionConfirmButton
|
||||||
|
key="cancel"
|
||||||
|
symbol={<XCircleIcon className="w-4" />}
|
||||||
|
label="Cancel"
|
||||||
|
onConfirm={() => {
|
||||||
|
router.push("/resources").catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<div className="mb-12">
|
||||||
|
<ResourceForm
|
||||||
|
methods={
|
||||||
|
formMethods as unknown as UseFormReturn<ResourceUpdateInput>
|
||||||
|
}
|
||||||
|
error={serverError}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</AdminBarLayout>
|
||||||
|
</HeaderFooterLayout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EditResourcePage;
|
@ -1,5 +1,5 @@
|
|||||||
import { LinkIcon } from "@heroicons/react/20/solid";
|
import { LinkIcon } from "@heroicons/react/20/solid";
|
||||||
import { PrinterIcon } from "@heroicons/react/24/solid";
|
import { PrinterIcon, PlusCircleIcon } from "@heroicons/react/24/solid";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import ResourceTable from "~/components/ResourceTable";
|
import ResourceTable from "~/components/ResourceTable";
|
||||||
@ -8,6 +8,42 @@ import { parseQueryData } from "~/utils/parseSearchForm";
|
|||||||
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
|
import { HeaderFooterLayout } from "~/layouts/HeaderFooterLayout";
|
||||||
import { type AuditoryResource } from "@prisma/client";
|
import { type AuditoryResource } from "@prisma/client";
|
||||||
import { QueryWaitWrapper } from "~/components/LoadingWrapper";
|
import { QueryWaitWrapper } from "~/components/LoadingWrapper";
|
||||||
|
import { AdminBarLayout } from "~/components/admin/ControlBar";
|
||||||
|
import { AdminActionLink } from "~/components/admin/common";
|
||||||
|
|
||||||
|
const PageHeader = ({ printLink }: { printLink: string }) => {
|
||||||
|
return (
|
||||||
|
<div className="mb-2 flex flex-row justify-between p-2 print:hidden sm:mb-4 sm:p-4">
|
||||||
|
<section className="space-y-2">
|
||||||
|
<h1 className="text-3xl font-bold">All Resources</h1>
|
||||||
|
<div className="">
|
||||||
|
<p className="inline">Fill out the </p>
|
||||||
|
<Link
|
||||||
|
href="/resources/search"
|
||||||
|
className="inline rounded-lg border border-neutral-800 bg-neutral-200 px-2 py-[4px] hover:bg-neutral-900 hover:text-white"
|
||||||
|
>
|
||||||
|
search form
|
||||||
|
<LinkIcon className="inline w-4" />
|
||||||
|
</Link>
|
||||||
|
<p className="inline">
|
||||||
|
{" "}
|
||||||
|
for a list of auditory training resource recommendations.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section className="mt-auto">
|
||||||
|
<Link
|
||||||
|
href={printLink}
|
||||||
|
className="inline-block whitespace-nowrap rounded-md border border-neutral-900 bg-yellow-200 px-4 py-2 align-middle font-semibold shadow shadow-black/50 duration-200 ease-out hover:bg-yellow-300 hover:shadow-md print:hidden sm:space-x-2"
|
||||||
|
>
|
||||||
|
<span className="hidden sm:inline-block">Print Results</span>
|
||||||
|
<PrinterIcon className="inline-block w-6" />
|
||||||
|
</Link>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const Resources = () => {
|
const Resources = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -49,39 +85,22 @@ const Resources = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<HeaderFooterLayout>
|
<HeaderFooterLayout>
|
||||||
|
<AdminBarLayout
|
||||||
|
actions={[
|
||||||
|
<AdminActionLink
|
||||||
|
key="cancel"
|
||||||
|
symbol={<PlusCircleIcon className="w-4" />}
|
||||||
|
label="Create New"
|
||||||
|
href={`/resources/create`}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
<div className="mx-auto mb-12 mt-6 w-full max-w-6xl md:px-2">
|
<div className="mx-auto mb-12 mt-6 w-full max-w-6xl md:px-2">
|
||||||
<div className="mb-2 flex flex-row justify-between p-2 print:hidden sm:mb-4 sm:p-4">
|
<PageHeader printLink={printLink} />
|
||||||
<section className="space-y-2">
|
|
||||||
<h1 className="text-3xl font-bold">All Resources</h1>
|
|
||||||
<div className="">
|
|
||||||
<p className="inline">Fill out the </p>
|
|
||||||
<Link
|
|
||||||
href="/resources/search"
|
|
||||||
className="inline rounded-lg border border-neutral-800 bg-neutral-200 px-2 py-[4px] hover:bg-neutral-900 hover:text-white"
|
|
||||||
>
|
|
||||||
search form
|
|
||||||
<LinkIcon className="inline w-4" />
|
|
||||||
</Link>
|
|
||||||
<p className="inline">
|
|
||||||
{" "}
|
|
||||||
for a list of auditory training resource recommendations.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section className="mt-auto">
|
|
||||||
<Link
|
|
||||||
href={printLink}
|
|
||||||
className="inline-block whitespace-nowrap rounded-md border border-neutral-900 bg-yellow-200 px-4 py-2 align-middle font-semibold shadow shadow-black/50 duration-200 ease-out hover:bg-yellow-300 hover:shadow-md print:hidden sm:space-x-2"
|
|
||||||
>
|
|
||||||
<span className="hidden sm:inline-block">Print Results</span>
|
|
||||||
<PrinterIcon className="inline-block w-6" />
|
|
||||||
</Link>
|
|
||||||
</section>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<QueryWaitWrapper query={resourceQuery} Render={ConditionalTable} />
|
<QueryWaitWrapper query={resourceQuery} Render={ConditionalTable} />
|
||||||
</div>
|
</div>
|
||||||
|
</AdminBarLayout>
|
||||||
</HeaderFooterLayout>
|
</HeaderFooterLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -16,6 +16,42 @@ const emptyStringToUndefined = (val: string | undefined | null) => {
|
|||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const AuditoryResourceSchema = z.object({
|
||||||
|
icon: z.string().min(1).optional().nullable(),
|
||||||
|
name: z.string().min(1),
|
||||||
|
description: z.string().min(1),
|
||||||
|
manufacturer: z.object({
|
||||||
|
name: z.string().min(1),
|
||||||
|
required: z.boolean().default(false),
|
||||||
|
notice: z.string().nullable().transform(emptyStringToUndefined),
|
||||||
|
}),
|
||||||
|
ages: z.object({ min: z.number().int(), max: z.number().int() }).refine(
|
||||||
|
(ages) => {
|
||||||
|
return ages.min < ages.max;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "Minimum supported age must be less than maximum supported age.",
|
||||||
|
}
|
||||||
|
),
|
||||||
|
skills: z.array(z.nativeEnum(Skill)),
|
||||||
|
skill_levels: z.array(z.nativeEnum(SkillLevel)),
|
||||||
|
payment_options: z.array(z.nativeEnum(PaymentType)),
|
||||||
|
photo: z
|
||||||
|
.object({
|
||||||
|
name: z.string(),
|
||||||
|
data: z.instanceof(Buffer),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
platform_links: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
platform: z.nativeEnum(Platform),
|
||||||
|
link: z.string().min(1),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.default([]),
|
||||||
|
});
|
||||||
|
|
||||||
export const auditoryResourceRouter = createTRPCRouter({
|
export const auditoryResourceRouter = createTRPCRouter({
|
||||||
byId: publicProcedure
|
byId: publicProcedure
|
||||||
.input(z.object({ id: z.string() }))
|
.input(z.object({ id: z.string() }))
|
||||||
@ -45,47 +81,30 @@ export const auditoryResourceRouter = createTRPCRouter({
|
|||||||
return ctx.prisma.auditoryResource.findMany();
|
return ctx.prisma.auditoryResource.findMany();
|
||||||
}),
|
}),
|
||||||
|
|
||||||
update: protectedProcedure
|
create: protectedProcedure
|
||||||
|
.input(AuditoryResourceSchema.strict())
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
return await ctx.prisma.auditoryResource.create({
|
||||||
|
data: input,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
delete: protectedProcedure
|
||||||
.input(
|
.input(
|
||||||
z.object({
|
z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
icon: z.string().min(1).optional(),
|
|
||||||
name: z.string().min(1).optional(),
|
|
||||||
description: z.string().min(1).optional(),
|
|
||||||
manufacturer: z
|
|
||||||
.object({
|
|
||||||
name: z.string().min(1),
|
|
||||||
required: z.boolean(),
|
|
||||||
notice: z
|
|
||||||
.string()
|
|
||||||
.optional()
|
|
||||||
.nullable()
|
|
||||||
.transform(emptyStringToUndefined),
|
|
||||||
})
|
|
||||||
.optional(),
|
|
||||||
ages: z
|
|
||||||
.object({ min: z.number().int(), max: z.number().int() })
|
|
||||||
.optional(),
|
|
||||||
skills: z.array(z.nativeEnum(Skill)).optional(),
|
|
||||||
skill_levels: z.array(z.nativeEnum(SkillLevel)).optional(),
|
|
||||||
payment_options: z.array(z.nativeEnum(PaymentType)).optional(),
|
|
||||||
photo: z
|
|
||||||
.object({
|
|
||||||
name: z.string(),
|
|
||||||
data: z.instanceof(Buffer),
|
|
||||||
})
|
|
||||||
.nullable()
|
|
||||||
.optional(),
|
|
||||||
platform_links: z
|
|
||||||
.array(
|
|
||||||
z.object({
|
|
||||||
platform: z.nativeEnum(Platform),
|
|
||||||
link: z.string().min(1),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.optional(),
|
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
.mutation(async ({ input, ctx }) => {
|
||||||
|
return await ctx.prisma.auditoryResource.delete({
|
||||||
|
where: {
|
||||||
|
id: input.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
update: protectedProcedure
|
||||||
|
.input(AuditoryResourceSchema.partial().extend({ id: z.string() }))
|
||||||
.mutation(async ({ input, ctx }) => {
|
.mutation(async ({ input, ctx }) => {
|
||||||
return await ctx.prisma.auditoryResource.update({
|
return await ctx.prisma.auditoryResource.update({
|
||||||
where: {
|
where: {
|
||||||
|
12
src/utils/parseTRPCError.ts
Normal file
12
src/utils/parseTRPCError.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const parseTRPCErrorMessage = (message: string) => {
|
||||||
|
try {
|
||||||
|
const zodErrors = JSON.parse(message) as unknown as { message: string }[];
|
||||||
|
return zodErrors
|
||||||
|
.map((error) => {
|
||||||
|
return error.message;
|
||||||
|
})
|
||||||
|
.join(", ");
|
||||||
|
} catch {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
};
|
@ -5,30 +5,30 @@ const config = {
|
|||||||
extend: {
|
extend: {
|
||||||
keyframes: {
|
keyframes: {
|
||||||
expand_in_out: {
|
expand_in_out: {
|
||||||
'0%, 100%': { transform: 'scale(1)' },
|
"0%, 100%": { transform: "scale(1)" },
|
||||||
'50%': { transform: 'scale(1.1)' },
|
"50%": { transform: "scale(1.1)" },
|
||||||
},
|
},
|
||||||
slide_left_full: {
|
slide_left_full: {
|
||||||
'0%': { transform: 'translate(0%)' },
|
"0%": { transform: "translate(0%)" },
|
||||||
'100%': { transform: 'translate(-50%)' },
|
"100%": { transform: "translate(-50%)" },
|
||||||
},
|
},
|
||||||
slide_right_full: {
|
slide_right_full: {
|
||||||
'0%': { transform: 'translate(-50%)' },
|
"0%": { transform: "translate(-50%)" },
|
||||||
'100%': { transform: 'translate(0%)' },
|
"100%": { transform: "translate(0%)" },
|
||||||
},
|
},
|
||||||
wiggle_rotate: {
|
wiggle_rotate: {
|
||||||
'0%, 60%, 100%': { transform: 'rotate(0deg)' },
|
"0%, 60%, 100%": { transform: "rotate(0deg)" },
|
||||||
'42%': { transform: 'rotate(-6deg)' },
|
"42%": { transform: "rotate(-6deg)" },
|
||||||
'50%': { transform: 'rotate(4deg)' },
|
"50%": { transform: "rotate(4deg)" },
|
||||||
'6%, 30%': { transform: 'rotate(24deg)' },
|
"6%, 30%": { transform: "rotate(24deg)" },
|
||||||
'18%': { transform: 'rotate(-12deg)' },
|
"18%": { transform: "rotate(-12deg)" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
expand_in_out: 'expand_in_out 2s ease-in-out infinite',
|
expand_in_out: "expand_in_out 2s ease-in-out infinite",
|
||||||
slide_search_page: 'slide_left_full 0.5s ease-in-out',
|
slide_search_page: "slide_left_full 0.5s ease-in-out",
|
||||||
slide_search_page_backwards: 'slide_right_full 0.5s ease-in-out',
|
slide_search_page_backwards: "slide_right_full 0.5s ease-in-out",
|
||||||
hand_wave: 'wiggle_rotate 1.5s ease-in-out infinite',
|
hand_wave: "wiggle_rotate 1.5s ease-in-out infinite",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user