Merge pull request #169 from sambecker/date-validation
Introduce date-time validation + customization
This commit is contained in:
commit
4ee1bd5e5e
17
README.md
17
README.md
@ -96,31 +96,34 @@ _⚠️ READ BEFORE PROCEEDING_
|
||||
|
||||
Application behavior can be changed by configuring the following environment variables:
|
||||
|
||||
#### Site meta
|
||||
#### Content
|
||||
- `NEXT_PUBLIC_SITE_TITLE` (seen in browser tab)
|
||||
- `NEXT_PUBLIC_SITE_DESCRIPTION` (seen in nav, beneath title)
|
||||
- `NEXT_PUBLIC_SITE_ABOUT` (seen in grid sidebar—accepts rich formatting tags: `<b>`, `<strong>`, `<i>`, `<em>`, `<u>`, `<br>`)
|
||||
|
||||
#### Site performance
|
||||
#### Performance
|
||||
> ⚠️ Enabling may result in increased project usage
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTOS = 1` enables static optimization for photo pages (`p/[photoId]`), i.e., renders pages at build time
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_OG_IMAGES = 1` enables static optimization for OG images, i.e., renders images at build time
|
||||
- `NEXT_PUBLIC_STATICALLY_OPTIMIZE_PHOTO_CATEGORIES = 1` enables static optimization for photo categories (`tag/[tag]`, `shot-on/[make]/[model]`, etc.), i.e., renders pages at build time
|
||||
- `NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS = 1` prevents photo uploads being compressed before storing
|
||||
|
||||
#### Site behavior
|
||||
#### Display
|
||||
- `NEXT_PUBLIC_HIDE_EXIF_DATA = 1` hides EXIF data in photo details and OG images (potentially useful for portfolios, which don't focus on photography)
|
||||
- `NEXT_PUBLIC_HIDE_TAKEN_AT_TIME = 1` hides taken at time from photo meta
|
||||
- `NEXT_PUBLIC_HIDE_SOCIAL = 1` removes X button from share modal
|
||||
- `NEXT_PUBLIC_HIDE_FILM_SIMULATIONS = 1` prevents Fujifilm simulations showing up in `/grid` sidebar and CMD-K search results
|
||||
- `NEXT_PUBLIC_HIDE_REPO_LINK = 1` removes footer link to repo
|
||||
|
||||
#### Settings
|
||||
- `NEXT_PUBLIC_GRID_HOMEPAGE = 1` shows grid layout on homepage
|
||||
- `NEXT_PUBLIC_DEFAULT_THEME = light | dark` sets preferred initial theme (defaults to `system` when not configured)
|
||||
- `NEXT_PUBLIC_MATTE_PHOTOS = 1` constrains the size of each photo, and enables a surrounding border (potentially useful for photos with tall aspect ratios)
|
||||
- `NEXT_PUBLIC_BLUR_DISABLED = 1` prevents image blur data being stored and displayed (potentially useful for limiting Postgres usage)
|
||||
- `NEXT_PUBLIC_GEO_PRIVACY = 1` disables collection/display of location-based data (⚠️ re-compresses uploaded images in order to remove GPS information)
|
||||
- `NEXT_PUBLIC_HIDE_REPO_LINK = 1` removes footer link to repo
|
||||
- `NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS = 1` enables public photo downloads for all visitors (⚠️ may result in increased bandwidth usage)
|
||||
- `NEXT_PUBLIC_PUBLIC_API = 1` enables public API available at `/api`
|
||||
- `NEXT_PUBLIC_IGNORE_PRIORITY_ORDER = 1` prevents `priority_order` field affecting photo order
|
||||
- `NEXT_PUBLIC_HIDE_SOCIAL = 1` removes X button from share modal
|
||||
- `NEXT_PUBLIC_HIDE_FILM_SIMULATIONS = 1` prevents Fujifilm simulations showing up in `/grid` sidebar and CMD-K search results
|
||||
- `NEXT_PUBLIC_HIDE_EXIF_DATA = 1` hides EXIF data in photo details and OG images (potentially useful for portfolios, which don't focus on photography)
|
||||
- `NEXT_PUBLIC_GRID_ASPECT_RATIO = 1.5` sets aspect ratio for grid tiles (defaults to `1`—setting to `0` removes the constraint)
|
||||
- `NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS = 1` ensures large thumbnails on photo grid views
|
||||
- `NEXT_PUBLIC_OG_TEXT_ALIGNMENT = BOTTOM` keeps OG image text bottom aligned (default is top)
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
/* eslint-disable max-len */
|
||||
import {
|
||||
convertTimestampToNaivePostgresString,
|
||||
convertTimestampWithOffsetToPostgresString,
|
||||
validatePostgresDateString,
|
||||
validateNaivePostgresDateString,
|
||||
} from '../src/utility/date';
|
||||
|
||||
describe('Date utility', () => {
|
||||
@ -29,19 +32,34 @@ describe('Date utility', () => {
|
||||
expect(convertTimestampToNaivePostgresString(timestamp))
|
||||
.toBe('2023-12-02 16:38:36');
|
||||
});
|
||||
it('Malformed date string', () => {
|
||||
const timestamp = '2024/01a/01 Z';
|
||||
expect(convertTimestampWithOffsetToPostgresString(timestamp))
|
||||
.toBe(convertTimestampWithOffsetToPostgresString(
|
||||
new Date().toISOString(),
|
||||
));
|
||||
});
|
||||
it('Empty string', () => {
|
||||
const timestamp = ' ';
|
||||
expect(convertTimestampWithOffsetToPostgresString(timestamp))
|
||||
.toBe(convertTimestampWithOffsetToPostgresString(
|
||||
new Date().toISOString(),
|
||||
));
|
||||
});
|
||||
});
|
||||
it('Malformed date string', () => {
|
||||
const timestamp = '2024/01a/01 Z';
|
||||
expect(convertTimestampWithOffsetToPostgresString(timestamp))
|
||||
.toBe(convertTimestampWithOffsetToPostgresString(
|
||||
new Date().toISOString(),
|
||||
));
|
||||
});
|
||||
it('Empty string', () => {
|
||||
const timestamp = ' ';
|
||||
expect(convertTimestampWithOffsetToPostgresString(timestamp))
|
||||
.toBe(convertTimestampWithOffsetToPostgresString(
|
||||
new Date().toISOString(),
|
||||
));
|
||||
describe('validates date strings', () => {
|
||||
it('Correct', () => {
|
||||
expect(validatePostgresDateString('2025-01-03T21:00:44.000Z')).toBe(true);
|
||||
expect(validateNaivePostgresDateString('2025-01-03 16:00:44')).toBe(true);
|
||||
});
|
||||
it('Incorrect', () => {
|
||||
expect(validatePostgresDateString('2024-01-01')).toBe(false);
|
||||
expect(validatePostgresDateString('2025-01-03 16:00:44')).toBe(false);
|
||||
expect(validateNaivePostgresDateString('2024-01-01')).toBe(false);
|
||||
expect(validatePostgresDateString('2025-01-03T21:00:44.000')).toBe(false);
|
||||
expect(validateNaivePostgresDateString('2025-01-03T16:00:44')).toBe(false);
|
||||
expect(validatePostgresDateString('2025-01-03T21:00:44.000ZZ')).toBe(false);
|
||||
expect(validateNaivePostgresDateString('2025-01-03 16:00:44Z')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
18
package.json
18
package.json
@ -9,18 +9,18 @@
|
||||
"analyze": "ANALYZE=true next build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^1.1.0",
|
||||
"@aws-sdk/client-s3": "3.732.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.732.0",
|
||||
"@radix-ui/react-dialog": "^1.1.4",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.4",
|
||||
"@ai-sdk/openai": "^1.1.1",
|
||||
"@aws-sdk/client-s3": "3.733.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.733.0",
|
||||
"@radix-ui/react-dialog": "^1.1.5",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.5",
|
||||
"@radix-ui/react-visually-hidden": "^1.1.1",
|
||||
"@upstash/ratelimit": "^2.0.5",
|
||||
"@vercel/analytics": "^1.4.1",
|
||||
"@vercel/blob": "^0.27.1",
|
||||
"@vercel/kv": "^3.0.0",
|
||||
"@vercel/speed-insights": "^1.1.0",
|
||||
"ai": "^4.1.0",
|
||||
"ai": "^4.1.1",
|
||||
"camelcase-keys": "^9.1.3",
|
||||
"cmdk": "^1.0.4",
|
||||
"date-fns": "^4.1.0",
|
||||
@ -28,7 +28,7 @@
|
||||
"exifr": "^7.1.3",
|
||||
"framer-motion": "^12.0.1",
|
||||
"nanoid": "^5.0.9",
|
||||
"next": "15.1.5",
|
||||
"next": "15.1.6",
|
||||
"next-auth": "5.0.0-beta.25",
|
||||
"next-themes": "^0.4.4",
|
||||
"pg": "^8.13.1",
|
||||
@ -43,7 +43,7 @@
|
||||
"use-debounce": "^10.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@next/bundle-analyzer": "15.1.5",
|
||||
"@next/bundle-analyzer": "15.1.6",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
@ -57,7 +57,7 @@
|
||||
"autoprefixer": "10.4.20",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint": "9.18.0",
|
||||
"eslint-config-next": "15.1.5",
|
||||
"eslint-config-next": "15.1.6",
|
||||
"jest": "^29.7.0",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"postcss": "8.5.1",
|
||||
|
||||
274
pnpm-lock.yaml
generated
274
pnpm-lock.yaml
generated
@ -9,20 +9,20 @@ importers:
|
||||
.:
|
||||
dependencies:
|
||||
'@ai-sdk/openai':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(zod@3.23.8)
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(zod@3.23.8)
|
||||
'@aws-sdk/client-s3':
|
||||
specifier: 3.732.0
|
||||
version: 3.732.0
|
||||
specifier: 3.733.0
|
||||
version: 3.733.0
|
||||
'@aws-sdk/s3-request-presigner':
|
||||
specifier: 3.732.0
|
||||
version: 3.732.0
|
||||
specifier: 3.733.0
|
||||
version: 3.733.0
|
||||
'@radix-ui/react-dialog':
|
||||
specifier: ^1.1.4
|
||||
version: 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: ^1.1.5
|
||||
version: 1.1.5(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-dropdown-menu':
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: ^2.1.5
|
||||
version: 2.1.5(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-visually-hidden':
|
||||
specifier: ^1.1.1
|
||||
version: 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
@ -31,7 +31,7 @@ importers:
|
||||
version: 2.0.5(@upstash/redis@1.34.3)
|
||||
'@vercel/analytics':
|
||||
specifier: ^1.4.1
|
||||
version: 1.4.1(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))
|
||||
version: 1.4.1(next@15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))
|
||||
'@vercel/blob':
|
||||
specifier: ^0.27.1
|
||||
version: 0.27.1
|
||||
@ -40,10 +40,10 @@ importers:
|
||||
version: 3.0.0
|
||||
'@vercel/speed-insights':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))
|
||||
version: 1.1.0(next@15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))
|
||||
ai:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(react@19.0.0)(zod@3.23.8)
|
||||
specifier: ^4.1.1
|
||||
version: 4.1.1(react@19.0.0)(zod@3.23.8)
|
||||
camelcase-keys:
|
||||
specifier: ^9.1.3
|
||||
version: 9.1.3
|
||||
@ -66,11 +66,11 @@ importers:
|
||||
specifier: ^5.0.9
|
||||
version: 5.0.9
|
||||
next:
|
||||
specifier: 15.1.5
|
||||
version: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
specifier: 15.1.6
|
||||
version: 15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
next-auth:
|
||||
specifier: 5.0.0-beta.25
|
||||
version: 5.0.0-beta.25(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
|
||||
version: 5.0.0-beta.25(next@15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)
|
||||
next-themes:
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
@ -106,8 +106,8 @@ importers:
|
||||
version: 10.0.4(react@19.0.0)
|
||||
devDependencies:
|
||||
'@next/bundle-analyzer':
|
||||
specifier: 15.1.5
|
||||
version: 15.1.5
|
||||
specifier: 15.1.6
|
||||
version: 15.1.6
|
||||
'@tailwindcss/container-queries':
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1(tailwindcss@3.4.17)
|
||||
@ -148,8 +148,8 @@ importers:
|
||||
specifier: 9.18.0
|
||||
version: 9.18.0(jiti@1.21.7)
|
||||
eslint-config-next:
|
||||
specifier: 15.1.5
|
||||
version: 15.1.5(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)
|
||||
specifier: 15.1.6
|
||||
version: 15.1.6(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)
|
||||
jest:
|
||||
specifier: ^29.7.0
|
||||
version: 29.7.0(@types/node@22.10.7)
|
||||
@ -171,14 +171,14 @@ packages:
|
||||
'@adobe/css-tools@4.4.0':
|
||||
resolution: {integrity: sha512-Ff9+ksdQQB3rMncgqDK78uLznstjyfIf2Arnh22pW8kBpLs6rpKDwgnZT46hin5Hl1WzazzK64DOrhSwYpS7bQ==}
|
||||
|
||||
'@ai-sdk/openai@1.1.0':
|
||||
resolution: {integrity: sha512-D2DaGMK89yYgO32n4Gr7gBJeJGGGS27gdfzYFMRDXlZmKh7VW1WXBp3FXxDwpmt0CgLoVI4qV8lf+gslah+kWw==}
|
||||
'@ai-sdk/openai@1.1.1':
|
||||
resolution: {integrity: sha512-0tUlrjSMWYYQxiC/6d6n5C6nxUYSHzlt/FipJgzKQleMts3Br5+u2cM4nwOVtuS14J2MsBM/SK2DGL0lFctirA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.0.0
|
||||
|
||||
'@ai-sdk/provider-utils@2.1.0':
|
||||
resolution: {integrity: sha512-rBUabNoyB25PBUjaiMSk86fHNSCqTngNZVvXxv8+6mvw47JX5OexW+ZHRsEw8XKTE8+hqvNFVzctaOrRZ2i9Zw==}
|
||||
'@ai-sdk/provider-utils@2.1.1':
|
||||
resolution: {integrity: sha512-+FRXSAdzPJFJN6TpyvyGWLo7WJuoBKI1g66UL+sli1HrxlldXSwxRPeb8tMMmNcyi3VKQogg2VsoJjlt4ort5w==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.0.0
|
||||
@ -186,12 +186,12 @@ packages:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@ai-sdk/provider@1.0.4':
|
||||
resolution: {integrity: sha512-lJi5zwDosvvZER3e/pB8lj1MN3o3S7zJliQq56BRr4e9V3fcRyFtwP0JRxaRS5vHYX3OJ154VezVoQNrk0eaKw==}
|
||||
'@ai-sdk/provider@1.0.5':
|
||||
resolution: {integrity: sha512-KATFp9CNXtMEzs8KBwLYK2+rGkkeED6p1+4koQveszyscIavObXIRW7vjr0MoZ9HFIHOUlrcak+3s/Xt3UXmAg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@ai-sdk/react@1.1.0':
|
||||
resolution: {integrity: sha512-U5lBbLyf1pw79xsk5dgHSkBv9Jta3xzWlOLpxsmHlxh1X94QOH3e1gm+nioQ/JvTuHLm23j2tz3i4MpMdchwXQ==}
|
||||
'@ai-sdk/react@1.1.1':
|
||||
resolution: {integrity: sha512-7LX/YF8sis8UM7p8ftUcu0xySG86/TBddcB42w/+mWOXL6hjYzcuGD8G121TobHsnxVxbsBlF/ykps/GYVvLNg==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
@ -202,8 +202,8 @@ packages:
|
||||
zod:
|
||||
optional: true
|
||||
|
||||
'@ai-sdk/ui-utils@1.1.0':
|
||||
resolution: {integrity: sha512-ETXwdHaHwzC7NIehbthDFGwsTFk+gNtRL/lm85nR4WDFvvYQptoM/7wTANs0p0H7zumB3Ep5hKzv0Encu8vSRw==}
|
||||
'@ai-sdk/ui-utils@1.1.1':
|
||||
resolution: {integrity: sha512-lkTxGoebnEgs8HtKeWut0AglXN7zpWQwYmun4yuhpiup7DxPWTmt3vGiYvqQTBOFAmyoea3uzIKjHwRHuayr2w==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
zod: ^3.0.0
|
||||
@ -256,8 +256,8 @@ packages:
|
||||
'@aws-crypto/util@5.2.0':
|
||||
resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==}
|
||||
|
||||
'@aws-sdk/client-s3@3.732.0':
|
||||
resolution: {integrity: sha512-ITPcG40qdiLXZRvNQ8V3u4yB16afcdIabdRBN6Blba31rk0MfeBGWwah0+lLSTFo1ZIrIQvBl6PAQ7mO0mkKLg==}
|
||||
'@aws-sdk/client-s3@3.733.0':
|
||||
resolution: {integrity: sha512-LmAbtNxrgbtB+YVt/HPPyKBgJWrvHOv5yNn98Ndlwm1mBgvI1N7+HQlI5ZWIKBCkwJtLtdS8ZVHzPtqnyWO+YA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@aws-sdk/client-sso@3.731.0':
|
||||
@ -324,8 +324,8 @@ packages:
|
||||
resolution: {integrity: sha512-y6FLASB1iKWuR5tUipMyo77bt0lEl3OnCrrd2xw/H24avq1HhJjjPR0HHhJE6QKJzF/FYXeV88tcyPSMe32VDw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@aws-sdk/middleware-sdk-s3@3.731.0':
|
||||
resolution: {integrity: sha512-J9aKyQaVoec5eWTSDfO4h2sKHNP0wTzN15LFcHnkD+e/d0rdmOi7BTkkbJrIaynma9WShIasmrtM3HNi9GiiTA==}
|
||||
'@aws-sdk/middleware-sdk-s3@3.733.0':
|
||||
resolution: {integrity: sha512-XX/sP61LugQZck6W8WQJpYQEeW/h7t0qgxfZEv9Qk9fWBxxdcR1j4zkmSD3Da5vgnBl8dJ3wdmI2k96qw6ONkQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@aws-sdk/middleware-ssec@3.731.0':
|
||||
@ -344,12 +344,12 @@ packages:
|
||||
resolution: {integrity: sha512-XlDpRNkDVHF59f07JmkuAidEv//m3hT6/JL85h0l3+zrpaRWhf8n8lVUyAPNq35ZujK8AcorYM+93u7hdWsliQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@aws-sdk/s3-request-presigner@3.732.0':
|
||||
resolution: {integrity: sha512-aIzl8UDZp1fNS6haLKmyHcLXg1vWhu9Yimz/9W1xElGB3XZc0LsTlp57yVTmx9ROYo3kAh+Z6RhF73bESTOmjA==}
|
||||
'@aws-sdk/s3-request-presigner@3.733.0':
|
||||
resolution: {integrity: sha512-EcB4b2cR/2yGUZXS4S7swEDaodImh4b4ndBYOfcKaP0m5A3cJ2evVjncKFOPLZmnIXRlqRXN1r0bW82uKxXnrA==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@aws-sdk/signature-v4-multi-region@3.731.0':
|
||||
resolution: {integrity: sha512-1r/b4Os15dR+BCVRRLVQJMF7Krq6xX6IKHxN43kuvODYWz8Nv3XDlaSpeRpAzyJuzW/fTp4JgE+z0+gmJfdEeA==}
|
||||
'@aws-sdk/signature-v4-multi-region@3.733.0':
|
||||
resolution: {integrity: sha512-gdN59yEDHSoEZqUJF4vnTl1OoiTfa8fyBWTbu4Ri1cYE20cFvoePHdz+eG6ipe7VZNwKf8j/ZQeOgO40jNbZKQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
|
||||
'@aws-sdk/token-providers@3.731.1':
|
||||
@ -844,59 +844,59 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.25':
|
||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==}
|
||||
|
||||
'@next/bundle-analyzer@15.1.5':
|
||||
resolution: {integrity: sha512-pCYMPgGRwf+FjEwUXFo3QF14VzBSPPsBHSFuXUpq5ifKcY8LbcmoF2xMVVMa2HoYgA1XuqPSAIfLJr4YXNa9xQ==}
|
||||
'@next/bundle-analyzer@15.1.6':
|
||||
resolution: {integrity: sha512-hGzQyDqJzFHcHNCyTqM3o05BpVq5tGnRODccZBVJDBf5Miv/26UJPMB0wh9L9j3ylgHC+0/v8BaBnBBek1rC6Q==}
|
||||
|
||||
'@next/env@15.1.5':
|
||||
resolution: {integrity: sha512-jg8ygVq99W3/XXb9Y6UQsritwhjc+qeiO7QrGZRYOfviyr/HcdnhdBQu4gbp2rBIh2ZyBYTBMWbPw3JSCb0GHw==}
|
||||
'@next/env@15.1.6':
|
||||
resolution: {integrity: sha512-d9AFQVPEYNr+aqokIiPLNK/MTyt3DWa/dpKveiAaVccUadFbhFEvY6FXYX2LJO2Hv7PHnLBu2oWwB4uBuHjr/w==}
|
||||
|
||||
'@next/eslint-plugin-next@15.1.5':
|
||||
resolution: {integrity: sha512-3cCrXBybsqe94UxD6DBQCYCCiP9YohBMgZ5IzzPYHmPzj8oqNlhBii5b6o1HDDaRHdz2pVnSsAROCtrczy8O0g==}
|
||||
'@next/eslint-plugin-next@15.1.6':
|
||||
resolution: {integrity: sha512-+slMxhTgILUntZDGNgsKEYHUvpn72WP1YTlkmEhS51vnVd7S9jEEy0n9YAMcI21vUG4akTw9voWH02lrClt/yw==}
|
||||
|
||||
'@next/swc-darwin-arm64@15.1.5':
|
||||
resolution: {integrity: sha512-5ttHGE75Nw9/l5S8zR2xEwR8OHEqcpPym3idIMAZ2yo+Edk0W/Vf46jGqPOZDk+m/SJ+vYZDSuztzhVha8rcdA==}
|
||||
'@next/swc-darwin-arm64@15.1.6':
|
||||
resolution: {integrity: sha512-u7lg4Mpl9qWpKgy6NzEkz/w0/keEHtOybmIl0ykgItBxEM5mYotS5PmqTpo+Rhg8FiOiWgwr8USxmKQkqLBCrw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-darwin-x64@15.1.5':
|
||||
resolution: {integrity: sha512-8YnZn7vDURUUTInfOcU5l0UWplZGBqUlzvqKKUFceM11SzfNEz7E28E1Arn4/FsOf90b1Nopboy7i7ufc4jXag==}
|
||||
'@next/swc-darwin-x64@15.1.6':
|
||||
resolution: {integrity: sha512-x1jGpbHbZoZ69nRuogGL2MYPLqohlhnT9OCU6E6QFewwup+z+M6r8oU47BTeJcWsF2sdBahp5cKiAcDbwwK/lg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@next/swc-linux-arm64-gnu@15.1.5':
|
||||
resolution: {integrity: sha512-rDJC4ctlYbK27tCyFUhgIv8o7miHNlpCjb2XXfTLQszwAUOSbcMN9q2y3urSrrRCyGVOd9ZR9a4S45dRh6JF3A==}
|
||||
'@next/swc-linux-arm64-gnu@15.1.6':
|
||||
resolution: {integrity: sha512-jar9sFw0XewXsBzPf9runGzoivajeWJUc/JkfbLTC4it9EhU8v7tCRLH7l5Y1ReTMN6zKJO0kKAGqDk8YSO2bg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-linux-arm64-musl@15.1.5':
|
||||
resolution: {integrity: sha512-FG5RApf4Gu+J+pHUQxXPM81oORZrKBYKUaBTylEIQ6Lz17hKVDsLbSXInfXM0giclvXbyiLXjTv42sQMATmZ0A==}
|
||||
'@next/swc-linux-arm64-musl@15.1.6':
|
||||
resolution: {integrity: sha512-+n3u//bfsrIaZch4cgOJ3tXCTbSxz0s6brJtU3SzLOvkJlPQMJ+eHVRi6qM2kKKKLuMY+tcau8XD9CJ1OjeSQQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-linux-x64-gnu@15.1.5':
|
||||
resolution: {integrity: sha512-NX2Ar3BCquAOYpnoYNcKz14eH03XuF7SmSlPzTSSU4PJe7+gelAjxo3Y7F2m8+hLT8ZkkqElawBp7SWBdzwqQw==}
|
||||
'@next/swc-linux-x64-gnu@15.1.6':
|
||||
resolution: {integrity: sha512-SpuDEXixM3PycniL4iVCLyUyvcl6Lt0mtv3am08sucskpG0tYkW1KlRhTgj4LI5ehyxriVVcfdoxuuP8csi3kQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-linux-x64-musl@15.1.5':
|
||||
resolution: {integrity: sha512-EQgqMiNu3mrV5eQHOIgeuh6GB5UU57tu17iFnLfBEhYfiOfyK+vleYKh2dkRVkV6ayx3eSqbIYgE7J7na4hhcA==}
|
||||
'@next/swc-linux-x64-musl@15.1.6':
|
||||
resolution: {integrity: sha512-L4druWmdFSZIIRhF+G60API5sFB7suTbDRhYWSjiw0RbE+15igQvE2g2+S973pMGvwN3guw7cJUjA/TmbPWTHQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@next/swc-win32-arm64-msvc@15.1.5':
|
||||
resolution: {integrity: sha512-HPULzqR/VqryQZbZME8HJE3jNFmTGcp+uRMHabFbQl63TtDPm+oCXAz3q8XyGv2AoihwNApVlur9Up7rXWRcjg==}
|
||||
'@next/swc-win32-arm64-msvc@15.1.6':
|
||||
resolution: {integrity: sha512-s8w6EeqNmi6gdvM19tqKKWbCyOBvXFbndkGHl+c9YrzsLARRdCHsD9S1fMj8gsXm9v8vhC8s3N8rjuC/XrtkEg==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@next/swc-win32-x64-msvc@15.1.5':
|
||||
resolution: {integrity: sha512-n74fUb/Ka1dZSVYfjwQ+nSJ+ifUff7jGurFcTuJNKZmI62FFOxQXUYit/uZXPTj2cirm1rvGWHG2GhbSol5Ikw==}
|
||||
'@next/swc-win32-x64-msvc@15.1.6':
|
||||
resolution: {integrity: sha512-6xomMuu54FAFxttYr5PJbEfu96godcxBTRk1OhAvJq0/EnmFU/Ybiax30Snis4vdWZ9LGpf7Roy5fSs7v/5ROQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@ -983,8 +983,8 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dialog@1.1.4':
|
||||
resolution: {integrity: sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==}
|
||||
'@radix-ui/react-dialog@1.1.5':
|
||||
resolution: {integrity: sha512-LaO3e5h/NOEL4OfXjxD43k9Dx+vn+8n+PCFt6uhX/BADFflllyv3WJG6rgvvSVBxpTch938Qq/LGc2MMxipXPw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
@ -1005,8 +1005,8 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.3':
|
||||
resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==}
|
||||
'@radix-ui/react-dismissable-layer@1.1.4':
|
||||
resolution: {integrity: sha512-XDUI0IVYVSwjMXxM6P4Dfti7AH+Y4oS/TB+sglZ/EXc7cqLwGAmp1NlMrcUjj7ks6R5WTZuWKv44FBbLpwU3sA==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
@ -1018,8 +1018,8 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-dropdown-menu@2.1.4':
|
||||
resolution: {integrity: sha512-iXU1Ab5ecM+yEepGAWK8ZhMyKX4ubFdCNtol4sT9D0OVErG9PNElfx3TQhjw7n7BC5nFVz68/5//clWy+8TXzA==}
|
||||
'@radix-ui/react-dropdown-menu@2.1.5':
|
||||
resolution: {integrity: sha512-50ZmEFL1kOuLalPKHrLWvPFMons2fGx9TqQCWlPwDVpbAnaUJ1g4XNcKqFNMQymYU0kKWR4MDDi+9vUQBGFgcQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
@ -1062,8 +1062,8 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-menu@2.1.4':
|
||||
resolution: {integrity: sha512-BnOgVoL6YYdHAG6DtXONaR29Eq4nvbi8rutrV/xlr3RQCMMb3yqP85Qiw/3NReozrSW+4dfLkK+rc1hb4wPU/A==}
|
||||
'@radix-ui/react-menu@2.1.5':
|
||||
resolution: {integrity: sha512-uH+3w5heoMJtqVCgYOtYVMECk1TOrkUn0OG0p5MqXC0W2ppcuVeESbou8PTHoqAjbdTEK19AGXBWcEtR5WpEQg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
@ -1766,8 +1766,8 @@ packages:
|
||||
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
|
||||
engines: {node: '>= 6.0.0'}
|
||||
|
||||
ai@4.1.0:
|
||||
resolution: {integrity: sha512-95nI9hBSSAKPrMnpJbaB3yqvh+G8BS4/EtFz3HR0HgEDJpxC0R6JAlB8+B/BXHd/roNGBrS08Z3Zain/6OFSYA==}
|
||||
ai@4.1.1:
|
||||
resolution: {integrity: sha512-oZTzQfrvrXuXnAJhoCsGcLUxSMWWYKkqrk2LfCzcukvB8us7ZUnFBgs9drhXuFP3JkWVbeIZHXjDexZIZcbi8g==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
react: ^18 || ^19 || ^19.0.0-rc
|
||||
@ -2367,8 +2367,8 @@ packages:
|
||||
engines: {node: '>=6.0'}
|
||||
hasBin: true
|
||||
|
||||
eslint-config-next@15.1.5:
|
||||
resolution: {integrity: sha512-Awm7iUJY8toOR+fU8yTxZnA7/LyOGUGOd6cENCuDfJ3gucHOSmLdOSGJ4u+nlrs8p5qXemua42bZmq+uOzxl6Q==}
|
||||
eslint-config-next@15.1.6:
|
||||
resolution: {integrity: sha512-Wd1uy6y7nBbXUSg9QAuQ+xYEKli5CgUhLjz1QHW11jLDis5vK5XB3PemL6jEmy7HrdhaRFDz+GTZ/3FoH+EUjg==}
|
||||
peerDependencies:
|
||||
eslint: ^7.23.0 || ^8.0.0 || ^9.0.0
|
||||
typescript: '>=3.3.1'
|
||||
@ -3331,8 +3331,8 @@ packages:
|
||||
react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc
|
||||
|
||||
next@15.1.5:
|
||||
resolution: {integrity: sha512-Cf/TEegnt01hn3Hoywh6N8fvkhbOuChO4wFje24+a86wKOubgVaWkDqxGVgoWlz2Hp9luMJ9zw3epftujdnUOg==}
|
||||
next@15.1.6:
|
||||
resolution: {integrity: sha512-Hch4wzbaX0vKQtalpXvUiw5sYivBy4cm5rzUKrBnUB/y436LGrvOUqYvlSeNVCWFO/770gDlltR9gqZH62ct4Q==}
|
||||
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -4381,39 +4381,39 @@ snapshots:
|
||||
|
||||
'@adobe/css-tools@4.4.0': {}
|
||||
|
||||
'@ai-sdk/openai@1.1.0(zod@3.23.8)':
|
||||
'@ai-sdk/openai@1.1.1(zod@3.23.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.4
|
||||
'@ai-sdk/provider-utils': 2.1.0(zod@3.23.8)
|
||||
'@ai-sdk/provider': 1.0.5
|
||||
'@ai-sdk/provider-utils': 2.1.1(zod@3.23.8)
|
||||
zod: 3.23.8
|
||||
|
||||
'@ai-sdk/provider-utils@2.1.0(zod@3.23.8)':
|
||||
'@ai-sdk/provider-utils@2.1.1(zod@3.23.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.4
|
||||
'@ai-sdk/provider': 1.0.5
|
||||
eventsource-parser: 3.0.0
|
||||
nanoid: 3.3.8
|
||||
secure-json-parse: 2.7.0
|
||||
optionalDependencies:
|
||||
zod: 3.23.8
|
||||
|
||||
'@ai-sdk/provider@1.0.4':
|
||||
'@ai-sdk/provider@1.0.5':
|
||||
dependencies:
|
||||
json-schema: 0.4.0
|
||||
|
||||
'@ai-sdk/react@1.1.0(react@19.0.0)(zod@3.23.8)':
|
||||
'@ai-sdk/react@1.1.1(react@19.0.0)(zod@3.23.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider-utils': 2.1.0(zod@3.23.8)
|
||||
'@ai-sdk/ui-utils': 1.1.0(zod@3.23.8)
|
||||
'@ai-sdk/provider-utils': 2.1.1(zod@3.23.8)
|
||||
'@ai-sdk/ui-utils': 1.1.1(zod@3.23.8)
|
||||
swr: 2.3.0(react@19.0.0)
|
||||
throttleit: 2.1.0
|
||||
optionalDependencies:
|
||||
react: 19.0.0
|
||||
zod: 3.23.8
|
||||
|
||||
'@ai-sdk/ui-utils@1.1.0(zod@3.23.8)':
|
||||
'@ai-sdk/ui-utils@1.1.1(zod@3.23.8)':
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.4
|
||||
'@ai-sdk/provider-utils': 2.1.0(zod@3.23.8)
|
||||
'@ai-sdk/provider': 1.0.5
|
||||
'@ai-sdk/provider-utils': 2.1.1(zod@3.23.8)
|
||||
zod-to-json-schema: 3.24.1(zod@3.23.8)
|
||||
optionalDependencies:
|
||||
zod: 3.23.8
|
||||
@ -4482,7 +4482,7 @@ snapshots:
|
||||
'@smithy/util-utf8': 2.3.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@aws-sdk/client-s3@3.732.0':
|
||||
'@aws-sdk/client-s3@3.733.0':
|
||||
dependencies:
|
||||
'@aws-crypto/sha1-browser': 5.2.0
|
||||
'@aws-crypto/sha256-browser': 5.2.0
|
||||
@ -4496,11 +4496,11 @@ snapshots:
|
||||
'@aws-sdk/middleware-location-constraint': 3.731.0
|
||||
'@aws-sdk/middleware-logger': 3.731.0
|
||||
'@aws-sdk/middleware-recursion-detection': 3.731.0
|
||||
'@aws-sdk/middleware-sdk-s3': 3.731.0
|
||||
'@aws-sdk/middleware-sdk-s3': 3.733.0
|
||||
'@aws-sdk/middleware-ssec': 3.731.0
|
||||
'@aws-sdk/middleware-user-agent': 3.731.0
|
||||
'@aws-sdk/region-config-resolver': 3.731.0
|
||||
'@aws-sdk/signature-v4-multi-region': 3.731.0
|
||||
'@aws-sdk/signature-v4-multi-region': 3.733.0
|
||||
'@aws-sdk/types': 3.731.0
|
||||
'@aws-sdk/util-endpoints': 3.731.0
|
||||
'@aws-sdk/util-user-agent-browser': 3.731.0
|
||||
@ -4748,7 +4748,7 @@ snapshots:
|
||||
'@smithy/types': 4.1.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@aws-sdk/middleware-sdk-s3@3.731.0':
|
||||
'@aws-sdk/middleware-sdk-s3@3.733.0':
|
||||
dependencies:
|
||||
'@aws-sdk/core': 3.731.0
|
||||
'@aws-sdk/types': 3.731.0
|
||||
@ -4833,9 +4833,9 @@ snapshots:
|
||||
'@smithy/util-middleware': 4.0.1
|
||||
tslib: 2.8.1
|
||||
|
||||
'@aws-sdk/s3-request-presigner@3.732.0':
|
||||
'@aws-sdk/s3-request-presigner@3.733.0':
|
||||
dependencies:
|
||||
'@aws-sdk/signature-v4-multi-region': 3.731.0
|
||||
'@aws-sdk/signature-v4-multi-region': 3.733.0
|
||||
'@aws-sdk/types': 3.731.0
|
||||
'@aws-sdk/util-format-url': 3.731.0
|
||||
'@smithy/middleware-endpoint': 4.0.1
|
||||
@ -4844,9 +4844,9 @@ snapshots:
|
||||
'@smithy/types': 4.1.0
|
||||
tslib: 2.8.1
|
||||
|
||||
'@aws-sdk/signature-v4-multi-region@3.731.0':
|
||||
'@aws-sdk/signature-v4-multi-region@3.733.0':
|
||||
dependencies:
|
||||
'@aws-sdk/middleware-sdk-s3': 3.731.0
|
||||
'@aws-sdk/middleware-sdk-s3': 3.733.0
|
||||
'@aws-sdk/types': 3.731.0
|
||||
'@smithy/protocol-http': 5.0.1
|
||||
'@smithy/signature-v4': 5.0.1
|
||||
@ -5469,41 +5469,41 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
|
||||
'@next/bundle-analyzer@15.1.5':
|
||||
'@next/bundle-analyzer@15.1.6':
|
||||
dependencies:
|
||||
webpack-bundle-analyzer: 4.10.1
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@next/env@15.1.5': {}
|
||||
'@next/env@15.1.6': {}
|
||||
|
||||
'@next/eslint-plugin-next@15.1.5':
|
||||
'@next/eslint-plugin-next@15.1.6':
|
||||
dependencies:
|
||||
fast-glob: 3.3.1
|
||||
|
||||
'@next/swc-darwin-arm64@15.1.5':
|
||||
'@next/swc-darwin-arm64@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-darwin-x64@15.1.5':
|
||||
'@next/swc-darwin-x64@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-gnu@15.1.5':
|
||||
'@next/swc-linux-arm64-gnu@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-arm64-musl@15.1.5':
|
||||
'@next/swc-linux-arm64-musl@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-gnu@15.1.5':
|
||||
'@next/swc-linux-x64-gnu@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-linux-x64-musl@15.1.5':
|
||||
'@next/swc-linux-x64-musl@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-arm64-msvc@15.1.5':
|
||||
'@next/swc-win32-arm64-msvc@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@next/swc-win32-x64-msvc@15.1.5':
|
||||
'@next/swc-win32-x64-msvc@15.1.6':
|
||||
optional: true
|
||||
|
||||
'@nodelib/fs.scandir@2.1.5':
|
||||
@ -5568,12 +5568,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.7
|
||||
|
||||
'@radix-ui/react-dialog@1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@radix-ui/react-dialog@1.1.5(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.1
|
||||
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0)
|
||||
@ -5596,7 +5596,7 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.7
|
||||
|
||||
'@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@radix-ui/react-dismissable-layer@1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.1
|
||||
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
@ -5609,13 +5609,13 @@ snapshots:
|
||||
'@types/react': 19.0.7
|
||||
'@types/react-dom': 19.0.3(@types/react@19.0.7)
|
||||
|
||||
'@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@radix-ui/react-dropdown-menu@2.1.5(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.1
|
||||
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-menu': 2.1.5(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.7)(react@19.0.0)
|
||||
react: 19.0.0
|
||||
@ -5648,14 +5648,14 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.7
|
||||
|
||||
'@radix-ui/react-menu@2.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
'@radix-ui/react-menu@2.1.5(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.1
|
||||
'@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-context': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-direction': 1.1.0(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-dismissable-layer': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0)
|
||||
@ -6389,9 +6389,9 @@ snapshots:
|
||||
dependencies:
|
||||
crypto-js: 4.2.0
|
||||
|
||||
'@vercel/analytics@1.4.1(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))':
|
||||
'@vercel/analytics@1.4.1(next@15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))':
|
||||
optionalDependencies:
|
||||
next: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
next: 15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
svelte: 4.2.17
|
||||
vue: 3.4.27(typescript@5.7.3)
|
||||
@ -6408,9 +6408,9 @@ snapshots:
|
||||
dependencies:
|
||||
'@upstash/redis': 1.34.3
|
||||
|
||||
'@vercel/speed-insights@1.1.0(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))':
|
||||
'@vercel/speed-insights@1.1.0(next@15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0)(svelte@4.2.17)(vue@3.4.27(typescript@5.7.3))':
|
||||
optionalDependencies:
|
||||
next: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
next: 15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
svelte: 4.2.17
|
||||
vue: 3.4.27(typescript@5.7.3)
|
||||
@ -6500,12 +6500,12 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
ai@4.1.0(react@19.0.0)(zod@3.23.8):
|
||||
ai@4.1.1(react@19.0.0)(zod@3.23.8):
|
||||
dependencies:
|
||||
'@ai-sdk/provider': 1.0.4
|
||||
'@ai-sdk/provider-utils': 2.1.0(zod@3.23.8)
|
||||
'@ai-sdk/react': 1.1.0(react@19.0.0)(zod@3.23.8)
|
||||
'@ai-sdk/ui-utils': 1.1.0(zod@3.23.8)
|
||||
'@ai-sdk/provider': 1.0.5
|
||||
'@ai-sdk/provider-utils': 2.1.1(zod@3.23.8)
|
||||
'@ai-sdk/react': 1.1.1(react@19.0.0)(zod@3.23.8)
|
||||
'@ai-sdk/ui-utils': 1.1.1(zod@3.23.8)
|
||||
'@opentelemetry/api': 1.9.0
|
||||
jsondiffpatch: 0.6.0
|
||||
optionalDependencies:
|
||||
@ -6837,7 +6837,7 @@ snapshots:
|
||||
|
||||
cmdk@1.0.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-dialog': 1.1.5(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@radix-ui/react-id': 1.1.0(@types/react@19.0.7)(react@19.0.0)
|
||||
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@19.0.3(@types/react@19.0.7))(@types/react@19.0.7)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
@ -7195,9 +7195,9 @@ snapshots:
|
||||
optionalDependencies:
|
||||
source-map: 0.6.1
|
||||
|
||||
eslint-config-next@15.1.5(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3):
|
||||
eslint-config-next@15.1.6(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3):
|
||||
dependencies:
|
||||
'@next/eslint-plugin-next': 15.1.5
|
||||
'@next/eslint-plugin-next': 15.1.6
|
||||
'@rushstack/eslint-patch': 1.10.3
|
||||
'@typescript-eslint/eslint-plugin': 8.19.0(@typescript-eslint/parser@8.19.0(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3))(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)
|
||||
'@typescript-eslint/parser': 8.19.0(eslint@9.18.0(jiti@1.21.7))(typescript@5.7.3)
|
||||
@ -8422,10 +8422,10 @@ snapshots:
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
||||
next-auth@5.0.0-beta.25(next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0):
|
||||
next-auth@5.0.0-beta.25(next@15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@auth/core': 0.37.2
|
||||
next: 15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
next: 15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
react: 19.0.0
|
||||
|
||||
next-themes@0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
@ -8433,9 +8433,9 @@ snapshots:
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
next@15.1.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
next@15.1.6(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
|
||||
dependencies:
|
||||
'@next/env': 15.1.5
|
||||
'@next/env': 15.1.6
|
||||
'@swc/counter': 0.1.3
|
||||
'@swc/helpers': 0.5.15
|
||||
busboy: 1.6.0
|
||||
@ -8445,14 +8445,14 @@ snapshots:
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
styled-jsx: 5.1.6(@babel/core@7.24.5)(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@next/swc-darwin-arm64': 15.1.5
|
||||
'@next/swc-darwin-x64': 15.1.5
|
||||
'@next/swc-linux-arm64-gnu': 15.1.5
|
||||
'@next/swc-linux-arm64-musl': 15.1.5
|
||||
'@next/swc-linux-x64-gnu': 15.1.5
|
||||
'@next/swc-linux-x64-musl': 15.1.5
|
||||
'@next/swc-win32-arm64-msvc': 15.1.5
|
||||
'@next/swc-win32-x64-msvc': 15.1.5
|
||||
'@next/swc-darwin-arm64': 15.1.6
|
||||
'@next/swc-darwin-x64': 15.1.6
|
||||
'@next/swc-linux-arm64-gnu': 15.1.6
|
||||
'@next/swc-linux-arm64-musl': 15.1.6
|
||||
'@next/swc-linux-x64-gnu': 15.1.6
|
||||
'@next/swc-linux-x64-musl': 15.1.6
|
||||
'@next/swc-win32-arm64-msvc': 15.1.6
|
||||
'@next/swc-win32-x64-msvc': 15.1.6
|
||||
'@opentelemetry/api': 1.9.0
|
||||
sharp: 0.33.5
|
||||
transitivePeerDependencies:
|
||||
|
||||
@ -58,7 +58,7 @@ export default function FieldSetWithStatus({
|
||||
{!hideLabel && label &&
|
||||
<label
|
||||
className={clsx(
|
||||
'flex gap-2 items-center select-none',
|
||||
'flex flex-wrap gap-x-2 items-center select-none',
|
||||
type === 'checkbox' && 'order-2 pt-[3px]',
|
||||
)}
|
||||
htmlFor={id}
|
||||
|
||||
@ -10,11 +10,13 @@ export default function ResponsiveDate({
|
||||
className,
|
||||
titleLabel,
|
||||
timezone: timezoneFromProps,
|
||||
hideTime,
|
||||
}: {
|
||||
date: Date
|
||||
className?: string
|
||||
titleLabel?: string
|
||||
timezone?: Timezone
|
||||
hideTime?: boolean,
|
||||
}) {
|
||||
const [timezone, setTimezone] = useState(timezoneFromProps);
|
||||
|
||||
@ -24,23 +26,30 @@ export default function ResponsiveDate({
|
||||
}
|
||||
}, [timezoneFromProps]);
|
||||
|
||||
const showPlaceholderContent = timezone === undefined;
|
||||
const showPlaceholder = timezone === undefined;
|
||||
|
||||
const titleDateFormatted = formatDate(date, undefined, timezone)
|
||||
const titleDateFormatted = formatDate({ date, timezone })
|
||||
.toLocaleUpperCase();
|
||||
|
||||
const title = titleLabel
|
||||
? `${titleLabel}: ${titleDateFormatted}`
|
||||
: titleDateFormatted;
|
||||
|
||||
const contentClass = showPlaceholderContent && 'opacity-0 select-none';
|
||||
const contentClass = showPlaceholder && 'opacity-0 select-none';
|
||||
|
||||
const formatDateProps = {
|
||||
date,
|
||||
timezone,
|
||||
showPlaceholder,
|
||||
hideTime,
|
||||
} as const;
|
||||
|
||||
return (
|
||||
<span
|
||||
title={showPlaceholderContent ? 'LOADING LOCAL TIME' : title}
|
||||
title={showPlaceholder ? 'LOADING LOCAL TIME' : title}
|
||||
className={clsx(
|
||||
'uppercase rounded-md transition-colors',
|
||||
showPlaceholderContent && 'bg-dim',
|
||||
showPlaceholder && 'bg-dim',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
@ -49,20 +58,20 @@ export default function ResponsiveDate({
|
||||
className={clsx('xs:hidden', contentClass)}
|
||||
aria-hidden
|
||||
>
|
||||
{formatDate(date, 'short', timezone, showPlaceholderContent)}
|
||||
{formatDate({ ...formatDateProps, length: 'short' })}
|
||||
</span>
|
||||
{/* Medium */}
|
||||
<span
|
||||
className={clsx('hidden xs:inline-block sm:hidden', contentClass)}
|
||||
aria-hidden
|
||||
>
|
||||
{formatDate(date, 'medium', timezone,showPlaceholderContent)}
|
||||
{formatDate({ ...formatDateProps, length: 'medium' })}
|
||||
</span>
|
||||
{/* Large */}
|
||||
<span
|
||||
className={clsx('hidden sm:inline-block', contentClass)}
|
||||
>
|
||||
{formatDate(date, undefined, timezone, showPlaceholderContent)}
|
||||
{formatDate(formatDateProps)}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
@ -8,11 +8,13 @@ export default function PhotoDate({
|
||||
className,
|
||||
dateType = 'takenAt',
|
||||
timezone,
|
||||
hideTime,
|
||||
}: {
|
||||
photo: Photo
|
||||
className?: string
|
||||
dateType?: 'takenAt' | 'createdAt' | 'updatedAt'
|
||||
timezone: Timezone
|
||||
hideTime?: boolean
|
||||
}) {
|
||||
const date = useMemo(() => {
|
||||
const date = new Date(dateType === 'takenAt'
|
||||
@ -45,6 +47,7 @@ export default function PhotoDate({
|
||||
className,
|
||||
titleLabel: getTitleLabel(),
|
||||
timezone,
|
||||
hideTime,
|
||||
}} />
|
||||
);
|
||||
}
|
||||
|
||||
@ -28,6 +28,7 @@ import PhotoLink from './PhotoLink';
|
||||
import {
|
||||
SHOULD_PREFETCH_ALL_LINKS,
|
||||
ALLOW_PUBLIC_DOWNLOADS,
|
||||
SHOW_TAKEN_AT_TIME,
|
||||
} from '@/site/config';
|
||||
import AdminPhotoMenuClient from '@/admin/AdminPhotoMenuClient';
|
||||
import { RevalidatePhoto } from './InfinitePhotoScroll';
|
||||
@ -248,8 +249,10 @@ export default function PhotoLarge({
|
||||
!hasNonDateContent && isUserSignedIn && 'md:pr-7',
|
||||
)}
|
||||
// Created at is a naive datetime which
|
||||
// does not require a timezone
|
||||
// does not require a timezone and will not
|
||||
// cause server/client time mismatch
|
||||
timezone={null}
|
||||
hideTime={!SHOW_TAKEN_AT_TIME}
|
||||
/>
|
||||
<div className={clsx(
|
||||
'flex gap-1 translate-y-[0.5px]',
|
||||
|
||||
@ -5,6 +5,8 @@ import {
|
||||
convertTimestampWithOffsetToPostgresString,
|
||||
generateLocalNaivePostgresString,
|
||||
generateLocalPostgresString,
|
||||
validationMessageNaivePostgresDateString,
|
||||
validationMessagePostgresDateString,
|
||||
} from '@/utility/date';
|
||||
import {
|
||||
convertApertureValueToFNumber,
|
||||
@ -116,8 +118,14 @@ const FORM_METADATA = (
|
||||
locationName: { label: 'location name', hide: true },
|
||||
latitude: { label: 'latitude' },
|
||||
longitude: { label: 'longitude' },
|
||||
takenAt: { label: 'taken at' },
|
||||
takenAtNaive: { label: 'taken at (naive)' },
|
||||
takenAt: {
|
||||
label: 'taken at',
|
||||
validate: validationMessagePostgresDateString,
|
||||
},
|
||||
takenAtNaive: {
|
||||
label: 'taken at (naive)',
|
||||
validate: validationMessageNaivePostgresDateString,
|
||||
},
|
||||
priorityOrder: { label: 'priority order' },
|
||||
favorite: { label: 'favorite', type: 'checkbox', excludeFromInsert: true },
|
||||
hidden: { label: 'hidden', type: 'checkbox' },
|
||||
|
||||
@ -212,7 +212,10 @@ export const titleForPhoto = (
|
||||
if (photo.title) {
|
||||
return photo.title;
|
||||
} else if (preferDateOverUntitled && (photo.takenAt || photo.createdAt)) {
|
||||
return formatDate(photo.takenAt || photo.createdAt, 'tiny');
|
||||
return formatDate({
|
||||
date: photo.takenAt || photo.createdAt,
|
||||
length: 'tiny',
|
||||
});
|
||||
} else {
|
||||
return 'Untitled';
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import {
|
||||
BiCog,
|
||||
BiCopy,
|
||||
BiData,
|
||||
BiHide,
|
||||
BiLockAlt,
|
||||
BiPencil,
|
||||
} from 'react-icons/bi';
|
||||
@ -29,7 +30,7 @@ import WarningNote from '@/components/WarningNote';
|
||||
import { RiSpeedMiniLine } from 'react-icons/ri';
|
||||
|
||||
export default function SiteChecklistClient({
|
||||
// Config checklist
|
||||
// Storage
|
||||
hasDatabase,
|
||||
isPostgresSslEnabled,
|
||||
hasVercelPostgres,
|
||||
@ -40,38 +41,49 @@ export default function SiteChecklistClient({
|
||||
hasAwsS3Storage,
|
||||
hasMultipleStorageProviders,
|
||||
currentStorage,
|
||||
// Auth
|
||||
hasAuthSecret,
|
||||
hasAdminUser,
|
||||
// Content
|
||||
hasDomain,
|
||||
hasTitle,
|
||||
hasDescription,
|
||||
hasAbout,
|
||||
hasDefaultTheme,
|
||||
showRepoLink,
|
||||
showSocial,
|
||||
showFilmSimulations,
|
||||
showExifInfo,
|
||||
// AI
|
||||
isAiTextGenerationEnabled,
|
||||
aiTextAutoGeneratedFields,
|
||||
hasAiTextAutoGeneratedFields,
|
||||
// Performance
|
||||
isStaticallyOptimized,
|
||||
arePhotosStaticallyOptimized,
|
||||
arePhotoOGImagesStaticallyOptimized,
|
||||
arePhotoCategoriesStaticallyOptimized,
|
||||
areOriginalUploadsPreserved,
|
||||
// Display
|
||||
showExifInfo,
|
||||
showTakenAtTimeHidden,
|
||||
showSocial,
|
||||
showFilmSimulations,
|
||||
showRepoLink,
|
||||
// Settings
|
||||
isGridHomepageEnabled,
|
||||
hasDefaultTheme,
|
||||
defaultTheme,
|
||||
arePhotosMatted,
|
||||
isBlurEnabled,
|
||||
isGeoPrivacyEnabled,
|
||||
isPriorityOrderEnabled,
|
||||
isAiTextGenerationEnabled,
|
||||
aiTextAutoGeneratedFields,
|
||||
hasAiTextAutoGeneratedFields,
|
||||
isPublicApiEnabled,
|
||||
arePublicDownloadsEnabled,
|
||||
isOgTextBottomAligned,
|
||||
gridAspectRatio,
|
||||
hasGridAspectRatio,
|
||||
gridDensity,
|
||||
hasGridDensityPreference,
|
||||
arePublicDownloadsEnabled,
|
||||
isPublicApiEnabled,
|
||||
isPriorityOrderEnabled,
|
||||
isOgTextBottomAligned,
|
||||
// Misc
|
||||
baseUrl,
|
||||
commitSha,
|
||||
commitMessage,
|
||||
// Connection status
|
||||
databaseError,
|
||||
storageError,
|
||||
@ -81,9 +93,6 @@ export default function SiteChecklistClient({
|
||||
simplifiedView,
|
||||
isTestingConnections,
|
||||
secret,
|
||||
baseUrl,
|
||||
commitSha,
|
||||
commitMessage,
|
||||
}: ConfigChecklistStatus &
|
||||
Partial<Awaited<ReturnType<typeof testConnectionsAction>>> & {
|
||||
simplifiedView?: boolean
|
||||
@ -473,6 +482,57 @@ export default function SiteChecklistClient({
|
||||
{renderEnvVars(['NEXT_PUBLIC_PRESERVE_ORIGINAL_UPLOADS'])}
|
||||
</ChecklistRow>
|
||||
</Checklist>
|
||||
<Checklist
|
||||
title="Display"
|
||||
icon={<BiHide size={18} />}
|
||||
optional
|
||||
>
|
||||
<ChecklistRow
|
||||
title="Show EXIF data"
|
||||
status={showExifInfo}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to hide EXIF data:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_EXIF_DATA'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show taken at time"
|
||||
status={showTakenAtTimeHidden}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to hide
|
||||
taken at time from photo meta:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_TAKEN_AT_TIME'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show social"
|
||||
status={showSocial}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to hide
|
||||
{' '}
|
||||
X button from share modal:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_SOCIAL'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show Fujifilm simulations"
|
||||
status={showFilmSimulations}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to prevent
|
||||
simulations showing up in /grid sidebar and
|
||||
CMD-K results:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_FILM_SIMULATIONS'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show repo link"
|
||||
status={showRepoLink}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to hide footer link:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_REPO_LINK'])}
|
||||
</ChecklistRow>
|
||||
</Checklist>
|
||||
<Checklist
|
||||
title="Settings"
|
||||
icon={<BiCog size={16} />}
|
||||
@ -528,12 +588,24 @@ export default function SiteChecklistClient({
|
||||
{renderEnvVars(['NEXT_PUBLIC_GEO_PRIVACY'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show repo link"
|
||||
status={showRepoLink}
|
||||
title={`Grid aspect ratio: ${gridAspectRatio}`}
|
||||
status={hasGridAspectRatio}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to hide footer link:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_REPO_LINK'])}
|
||||
Set environment variable to any number to enforce aspect ratio
|
||||
{' '}
|
||||
(default is {'"1"'}, i.e., square)—set to {'"0"'} to disable:
|
||||
{renderEnvVars(['NEXT_PUBLIC_GRID_ASPECT_RATIO'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title={`Grid density: ${gridDensity ? 'low' : 'high'}`}
|
||||
status={hasGridDensityPreference}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to ensure large thumbnails
|
||||
on photo grid views (if not configured, density is based on
|
||||
aspect ratio configuration):
|
||||
{renderEnvVars(['NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Public downloads"
|
||||
@ -562,54 +634,6 @@ export default function SiteChecklistClient({
|
||||
priority order photo field affecting photo order:
|
||||
{renderEnvVars(['NEXT_PUBLIC_IGNORE_PRIORITY_ORDER'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show social"
|
||||
status={showSocial}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to hide
|
||||
{' '}
|
||||
X button from share modal:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_SOCIAL'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show Fujifilm simulations"
|
||||
status={showFilmSimulations}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to prevent
|
||||
simulations showing up in /grid sidebar and
|
||||
CMD-K results:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_FILM_SIMULATIONS'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Show EXIF data"
|
||||
status={showExifInfo}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to hide EXIF data:
|
||||
{renderEnvVars(['NEXT_PUBLIC_HIDE_EXIF_DATA'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title={`Grid aspect ratio: ${gridAspectRatio}`}
|
||||
status={hasGridAspectRatio}
|
||||
optional
|
||||
>
|
||||
Set environment variable to any number to enforce aspect ratio
|
||||
{' '}
|
||||
(default is {'"1"'}, i.e., square)—set to {'"0"'} to disable:
|
||||
{renderEnvVars(['NEXT_PUBLIC_GRID_ASPECT_RATIO'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title={`Grid density: ${gridDensity ? 'low' : 'high'}`}
|
||||
status={hasGridDensityPreference}
|
||||
optional
|
||||
>
|
||||
Set environment variable to {'"1"'} to ensure large thumbnails
|
||||
on photo grid views (if not configured, density is based on
|
||||
aspect ratio configuration):
|
||||
{renderEnvVars(['NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS'])}
|
||||
</ChecklistRow>
|
||||
<ChecklistRow
|
||||
title="Legacy OG text alignment"
|
||||
status={isOgTextBottomAligned}
|
||||
|
||||
@ -122,6 +122,13 @@ export const CURRENT_STORAGE: StorageType =
|
||||
: 'vercel-blob'
|
||||
);
|
||||
|
||||
// AI
|
||||
|
||||
export const AI_TEXT_GENERATION_ENABLED =
|
||||
Boolean(process.env.OPENAI_SECRET_KEY);
|
||||
export const AI_TEXT_AUTO_GENERATED_FIELDS = parseAiAutoGeneratedFieldsText(
|
||||
process.env.AI_TEXT_AUTO_GENERATED_FIELDS);
|
||||
|
||||
// PERFORMANCE
|
||||
|
||||
export const STATICALLY_OPTIMIZED_PHOTOS =
|
||||
@ -139,6 +146,19 @@ export const PRESERVE_ORIGINAL_UPLOADS =
|
||||
// Legacy environment variable name
|
||||
process.env.NEXT_PUBLIC_PRO_MODE === '1';
|
||||
|
||||
// DISPLAY
|
||||
|
||||
export const SHOW_EXIF_DATA =
|
||||
process.env.NEXT_PUBLIC_HIDE_EXIF_DATA !== '1';
|
||||
export const SHOW_TAKEN_AT_TIME =
|
||||
process.env.NEXT_PUBLIC_HIDE_TAKEN_AT_TIME !== '1';
|
||||
export const SHOW_SOCIAL =
|
||||
process.env.NEXT_PUBLIC_HIDE_SOCIAL !== '1';
|
||||
export const SHOW_FILM_SIMULATIONS =
|
||||
process.env.NEXT_PUBLIC_HIDE_FILM_SIMULATIONS !== '1';
|
||||
export const SHOW_REPO_LINK =
|
||||
process.env.NEXT_PUBLIC_HIDE_REPO_LINK !== '1';
|
||||
|
||||
// SETTINGS
|
||||
|
||||
export const GRID_HOMEPAGE_ENABLED =
|
||||
@ -155,39 +175,30 @@ export const BLUR_ENABLED =
|
||||
process.env.NEXT_PUBLIC_BLUR_DISABLED !== '1';
|
||||
export const GEO_PRIVACY_ENABLED =
|
||||
process.env.NEXT_PUBLIC_GEO_PRIVACY === '1';
|
||||
export const AI_TEXT_GENERATION_ENABLED =
|
||||
Boolean(process.env.OPENAI_SECRET_KEY);
|
||||
export const AI_TEXT_AUTO_GENERATED_FIELDS = parseAiAutoGeneratedFieldsText(
|
||||
process.env.AI_TEXT_AUTO_GENERATED_FIELDS);
|
||||
export const PRIORITY_ORDER_ENABLED =
|
||||
process.env.NEXT_PUBLIC_IGNORE_PRIORITY_ORDER !== '1';
|
||||
export const PUBLIC_API_ENABLED =
|
||||
process.env.NEXT_PUBLIC_PUBLIC_API === '1';
|
||||
export const ALLOW_PUBLIC_DOWNLOADS =
|
||||
process.env.NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS === '1';
|
||||
export const SHOW_REPO_LINK =
|
||||
process.env.NEXT_PUBLIC_HIDE_REPO_LINK !== '1';
|
||||
export const SHOW_SOCIAL =
|
||||
process.env.NEXT_PUBLIC_HIDE_SOCIAL !== '1';
|
||||
export const SHOW_FILM_SIMULATIONS =
|
||||
process.env.NEXT_PUBLIC_HIDE_FILM_SIMULATIONS !== '1';
|
||||
export const SHOW_EXIF_DATA =
|
||||
process.env.NEXT_PUBLIC_HIDE_EXIF_DATA !== '1';
|
||||
export const GRID_ASPECT_RATIO =
|
||||
process.env.NEXT_PUBLIC_GRID_ASPECT_RATIO
|
||||
? parseFloat(process.env.NEXT_PUBLIC_GRID_ASPECT_RATIO)
|
||||
: 1;
|
||||
export const OG_TEXT_BOTTOM_ALIGNMENT =
|
||||
(process.env.NEXT_PUBLIC_OG_TEXT_ALIGNMENT ?? '').toUpperCase() === 'BOTTOM';
|
||||
export const ADMIN_DEBUG_TOOLS_ENABLED = process.env.ADMIN_DEBUG_TOOLS === '1';
|
||||
|
||||
export const PREFERS_LOW_DENSITY_GRID =
|
||||
process.env.NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS === '1';
|
||||
export const HIGH_DENSITY_GRID =
|
||||
GRID_ASPECT_RATIO <= 1 &&
|
||||
!PREFERS_LOW_DENSITY_GRID;
|
||||
export const ALLOW_PUBLIC_DOWNLOADS =
|
||||
process.env.NEXT_PUBLIC_ALLOW_PUBLIC_DOWNLOADS === '1';
|
||||
export const PUBLIC_API_ENABLED =
|
||||
process.env.NEXT_PUBLIC_PUBLIC_API === '1';
|
||||
export const PRIORITY_ORDER_ENABLED =
|
||||
process.env.NEXT_PUBLIC_IGNORE_PRIORITY_ORDER !== '1';
|
||||
export const OG_TEXT_BOTTOM_ALIGNMENT =
|
||||
(process.env.NEXT_PUBLIC_OG_TEXT_ALIGNMENT ?? '').toUpperCase() === 'BOTTOM';
|
||||
|
||||
// INTERNAL
|
||||
|
||||
export const ADMIN_DEBUG_TOOLS_ENABLED = process.env.ADMIN_DEBUG_TOOLS === '1';
|
||||
|
||||
export const CONFIG_CHECKLIST_STATUS = {
|
||||
// STORAGE
|
||||
hasDatabase: HAS_DATABASE,
|
||||
isPostgresSslEnabled: POSTGRES_SSL_ENABLED,
|
||||
hasVercelPostgres: (
|
||||
@ -205,20 +216,27 @@ export const CONFIG_CHECKLIST_STATUS = {
|
||||
),
|
||||
hasMultipleStorageProviders: HAS_MULTIPLE_STORAGE_PROVIDERS,
|
||||
currentStorage: CURRENT_STORAGE,
|
||||
// AUTH
|
||||
hasAuthSecret: Boolean(process.env.AUTH_SECRET),
|
||||
hasAdminUser: (
|
||||
Boolean(process.env.ADMIN_EMAIL) &&
|
||||
Boolean(process.env.ADMIN_PASSWORD)
|
||||
),
|
||||
// CONTENT
|
||||
hasDomain: Boolean(process.env.NEXT_PUBLIC_SITE_DOMAIN),
|
||||
hasTitle: Boolean(process.env.NEXT_PUBLIC_SITE_TITLE),
|
||||
hasDescription: HAS_DEFINED_SITE_DESCRIPTION,
|
||||
hasAbout: Boolean(process.env.NEXT_PUBLIC_SITE_ABOUT),
|
||||
hasDefaultTheme: Boolean(process.env.NEXT_PUBLIC_DEFAULT_THEME),
|
||||
showRepoLink: SHOW_REPO_LINK,
|
||||
showSocial: SHOW_SOCIAL,
|
||||
showFilmSimulations: SHOW_FILM_SIMULATIONS,
|
||||
showExifInfo: SHOW_EXIF_DATA,
|
||||
// AI
|
||||
isAiTextGenerationEnabled: AI_TEXT_GENERATION_ENABLED,
|
||||
aiTextAutoGeneratedFields: process.env.AI_TEXT_AUTO_GENERATED_FIELDS
|
||||
? AI_TEXT_AUTO_GENERATED_FIELDS.length === 0
|
||||
? ['none']
|
||||
: AI_TEXT_AUTO_GENERATED_FIELDS
|
||||
: ['all'],
|
||||
hasAiTextAutoGeneratedFields:
|
||||
Boolean(process.env.AI_TEXT_AUTO_GENERATED_FIELDS),
|
||||
// PERFORMANCE
|
||||
isStaticallyOptimized: (
|
||||
STATICALLY_OPTIMIZED_PHOTOS ||
|
||||
STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES ||
|
||||
@ -228,28 +246,29 @@ export const CONFIG_CHECKLIST_STATUS = {
|
||||
arePhotoOGImagesStaticallyOptimized: STATICALLY_OPTIMIZED_PHOTO_OG_IMAGES,
|
||||
arePhotoCategoriesStaticallyOptimized: STATICALLY_OPTIMIZED_PHOTO_CATEGORIES,
|
||||
areOriginalUploadsPreserved: PRESERVE_ORIGINAL_UPLOADS,
|
||||
// DISPLAY
|
||||
showExifInfo: SHOW_EXIF_DATA,
|
||||
showTakenAtTimeHidden: SHOW_TAKEN_AT_TIME,
|
||||
showSocial: SHOW_SOCIAL,
|
||||
showFilmSimulations: SHOW_FILM_SIMULATIONS,
|
||||
showRepoLink: SHOW_REPO_LINK,
|
||||
// SETTINGS
|
||||
isGridHomepageEnabled: GRID_HOMEPAGE_ENABLED,
|
||||
hasDefaultTheme: Boolean(process.env.NEXT_PUBLIC_DEFAULT_THEME),
|
||||
defaultTheme: DEFAULT_THEME,
|
||||
arePhotosMatted: MATTE_PHOTOS,
|
||||
isBlurEnabled: BLUR_ENABLED,
|
||||
isGeoPrivacyEnabled: GEO_PRIVACY_ENABLED,
|
||||
isAiTextGenerationEnabled: AI_TEXT_GENERATION_ENABLED,
|
||||
aiTextAutoGeneratedFields: process.env.AI_TEXT_AUTO_GENERATED_FIELDS
|
||||
? AI_TEXT_AUTO_GENERATED_FIELDS.length === 0
|
||||
? ['none']
|
||||
: AI_TEXT_AUTO_GENERATED_FIELDS
|
||||
: ['all'],
|
||||
hasAiTextAutoGeneratedFields:
|
||||
Boolean(process.env.AI_TEXT_AUTO_GENERATED_FIELDS),
|
||||
isPriorityOrderEnabled: PRIORITY_ORDER_ENABLED,
|
||||
isPublicApiEnabled: PUBLIC_API_ENABLED,
|
||||
arePublicDownloadsEnabled: ALLOW_PUBLIC_DOWNLOADS,
|
||||
isOgTextBottomAligned: OG_TEXT_BOTTOM_ALIGNMENT,
|
||||
gridAspectRatio: GRID_ASPECT_RATIO,
|
||||
hasGridAspectRatio: Boolean(process.env.NEXT_PUBLIC_GRID_ASPECT_RATIO),
|
||||
gridDensity: HIGH_DENSITY_GRID,
|
||||
hasGridDensityPreference:
|
||||
Boolean(process.env.NEXT_PUBLIC_SHOW_LARGE_THUMBNAILS),
|
||||
arePublicDownloadsEnabled: ALLOW_PUBLIC_DOWNLOADS,
|
||||
isPublicApiEnabled: PUBLIC_API_ENABLED,
|
||||
isPriorityOrderEnabled: PRIORITY_ORDER_ENABLED,
|
||||
isOgTextBottomAligned: OG_TEXT_BOTTOM_ALIGNMENT,
|
||||
// MISC
|
||||
baseUrl: BASE_URL,
|
||||
commitSha: VERCEL_COMMIT_SHA ? VERCEL_COMMIT_SHA.slice(0, 7) : undefined,
|
||||
commitMessage: VERCEL_COMMIT_MESSAGE,
|
||||
|
||||
@ -2,56 +2,78 @@ import { parseISO, parse, format } from 'date-fns';
|
||||
import { formatInTimeZone } from 'date-fns-tz';
|
||||
import { Timezone } from './timezone';
|
||||
|
||||
const DATE_STRING_FORMAT_TINY = 'dd MMM yy';
|
||||
const DATE_STRING_FORMAT_TINY_PLACEHOLDER = '00 000 00';
|
||||
const DATE_STRING_FORMAT_TINY = 'dd MMM yy';
|
||||
const DATE_STRING_FORMAT_TINY_PLACEHOLDER = '00 000 00';
|
||||
|
||||
const DATE_STRING_FORMAT_SHORT = 'dd MMM yyyy';
|
||||
const DATE_STRING_FORMAT_SHORT_PLACEHOLDER = '00 000 0000';
|
||||
const DATE_STRING_FORMAT_SHORT = 'dd MMM yyyy';
|
||||
const DATE_STRING_FORMAT_SHORT_PLACEHOLDER = '00 000 0000';
|
||||
|
||||
const DATE_STRING_FORMAT_MEDIUM = 'dd MMM yy h:mma';
|
||||
const DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER = '00 000 00 00:0000';
|
||||
const DATE_STRING_FORMAT_MEDIUM = 'dd MMM yy h:mma';
|
||||
const DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER = '00 000 00 00:0000';
|
||||
|
||||
const DATE_STRING_FORMAT_LONG = 'dd MMM yyyy h:mma';
|
||||
const DATE_STRING_FORMAT_LONG_PLACEHOLDER = '00 000 0000 00:0000';
|
||||
const DATE_STRING_FORMAT_LONG = 'dd MMM yyyy h:mma';
|
||||
const DATE_STRING_FORMAT_LONG_PLACEHOLDER = '00 000 0000 00:0000';
|
||||
|
||||
const DATE_STRING_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss';
|
||||
const DATE_STRING_FORMAT_POSTGRES = 'yyyy-MM-dd HH:mm:ss';
|
||||
|
||||
export const VALIDATION_EXAMPLE_POSTGRES = '2025-01-03T21:00:44.000Z';
|
||||
export const VALIDATION_EXAMPLE_POSTGRES_NAIVE = '2025-01-03 16:00:44';
|
||||
|
||||
type AmbiguousTimestamp = number | string;
|
||||
|
||||
type Length = 'tiny' | 'short' | 'medium' | 'long';
|
||||
|
||||
export const formatDate = (
|
||||
export const formatDate = ({
|
||||
date,
|
||||
length = 'long',
|
||||
timezone,
|
||||
hideTime,
|
||||
showPlaceholder,
|
||||
}: {
|
||||
date: Date,
|
||||
length: Length = 'long',
|
||||
length?: Length,
|
||||
timezone?: Timezone,
|
||||
hideTime?: boolean,
|
||||
showPlaceholder?: boolean,
|
||||
) => {
|
||||
switch (length) {
|
||||
case 'tiny': return showPlaceholder
|
||||
? DATE_STRING_FORMAT_TINY_PLACEHOLDER
|
||||
: timezone
|
||||
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_TINY)
|
||||
: format(date, DATE_STRING_FORMAT_TINY);
|
||||
case 'short': return showPlaceholder
|
||||
? DATE_STRING_FORMAT_SHORT_PLACEHOLDER
|
||||
: timezone
|
||||
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_SHORT)
|
||||
: format(date, DATE_STRING_FORMAT_SHORT);
|
||||
case 'medium': return showPlaceholder
|
||||
? DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER
|
||||
: timezone
|
||||
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_MEDIUM)
|
||||
: format(date, DATE_STRING_FORMAT_MEDIUM);
|
||||
default: return showPlaceholder
|
||||
}) => {
|
||||
let formatString = !hideTime
|
||||
? DATE_STRING_FORMAT_LONG
|
||||
: DATE_STRING_FORMAT_SHORT;
|
||||
let placeholderString = !hideTime
|
||||
? DATE_STRING_FORMAT_LONG_PLACEHOLDER
|
||||
: timezone
|
||||
? formatInTimeZone(date, timezone, DATE_STRING_FORMAT_LONG)
|
||||
: format(date, DATE_STRING_FORMAT_LONG);
|
||||
: DATE_STRING_FORMAT_SHORT_PLACEHOLDER;
|
||||
|
||||
switch (length) {
|
||||
case 'tiny':
|
||||
formatString = DATE_STRING_FORMAT_TINY;
|
||||
placeholderString = DATE_STRING_FORMAT_TINY_PLACEHOLDER;
|
||||
break;
|
||||
case 'short':
|
||||
formatString = DATE_STRING_FORMAT_SHORT;
|
||||
placeholderString = DATE_STRING_FORMAT_SHORT_PLACEHOLDER;
|
||||
break;
|
||||
case 'medium':
|
||||
formatString = !hideTime
|
||||
? DATE_STRING_FORMAT_MEDIUM
|
||||
: DATE_STRING_FORMAT_SHORT;
|
||||
placeholderString = !hideTime
|
||||
? DATE_STRING_FORMAT_MEDIUM_PLACEHOLDER
|
||||
: DATE_STRING_FORMAT_SHORT_PLACEHOLDER;
|
||||
break;
|
||||
}
|
||||
|
||||
return showPlaceholder
|
||||
? placeholderString
|
||||
: timezone
|
||||
? formatInTimeZone(date, timezone, formatString)
|
||||
: format(date, formatString);
|
||||
};
|
||||
|
||||
export const formatDateFromPostgresString = (date: string, length?: Length) =>
|
||||
formatDate(parse(date, DATE_STRING_FORMAT_POSTGRES, new Date()), length);
|
||||
formatDate({
|
||||
date: parse(date, DATE_STRING_FORMAT_POSTGRES, new Date()),
|
||||
length,
|
||||
});
|
||||
|
||||
export const formatDateForPostgres = (date: Date) =>
|
||||
date.toISOString().replace(
|
||||
@ -103,3 +125,23 @@ export const generateLocalPostgresString = () =>
|
||||
|
||||
export const generateLocalNaivePostgresString = () =>
|
||||
format(new Date(), DATE_STRING_FORMAT_POSTGRES);
|
||||
|
||||
// Form validation to prevent Postgres runtime errors
|
||||
|
||||
// POSTGRES: 2025-01-03T21:00:44.000Z
|
||||
export const validatePostgresDateString = (date = ''): boolean =>
|
||||
/^(\d{4}-\d{2}-\d{2})T\d{2}:\d{2}:\d{2}(.[\d]+)*Z$/.test(date);
|
||||
|
||||
export const validationMessagePostgresDateString = (date = '') =>
|
||||
validatePostgresDateString(date)
|
||||
? undefined
|
||||
: `Invalid format (${VALIDATION_EXAMPLE_POSTGRES})`;
|
||||
|
||||
// NAIVE: 2025-01-03 16:00:44
|
||||
export const validateNaivePostgresDateString = (date = ''): boolean =>
|
||||
/^(\d{4}-\d{2}-\d{2}) \d{2}:\d{2}:\d{2}$/.test(date);
|
||||
|
||||
export const validationMessageNaivePostgresDateString = (date = '') =>
|
||||
validateNaivePostgresDateString(date)
|
||||
? undefined
|
||||
: `Invalid format (${VALIDATION_EXAMPLE_POSTGRES_NAIVE})`;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user