Add ••• admin button to individual photos
This commit is contained in:
parent
74ca2ba383
commit
47ebc65553
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -14,6 +14,7 @@
|
||||
"favs",
|
||||
"ghijklmnopqrstuv",
|
||||
"Hasselblad",
|
||||
"headlessui",
|
||||
"hgetall",
|
||||
"hset",
|
||||
"Lightbox",
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.485.0",
|
||||
"@aws-sdk/s3-request-presigner": "3.485.0",
|
||||
"@headlessui/react": "2.0.0-alpha.4",
|
||||
"@next/bundle-analyzer": "14.0.4",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@testing-library/jest-dom": "^6.2.0",
|
||||
|
||||
198
pnpm-lock.yaml
generated
198
pnpm-lock.yaml
generated
@ -11,6 +11,9 @@ dependencies:
|
||||
'@aws-sdk/s3-request-presigner':
|
||||
specifier: 3.485.0
|
||||
version: 3.485.0
|
||||
'@headlessui/react':
|
||||
specifier: 2.0.0-alpha.4
|
||||
version: 2.0.0-alpha.4(react-dom@18.2.0)(react@18.2.0)
|
||||
'@next/bundle-analyzer':
|
||||
specifier: 14.0.4
|
||||
version: 14.0.4
|
||||
@ -1159,6 +1162,62 @@ packages:
|
||||
engines: {node: '>=14'}
|
||||
dev: false
|
||||
|
||||
/@floating-ui/core@1.5.3:
|
||||
resolution: {integrity: sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==}
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.2.1
|
||||
dev: false
|
||||
|
||||
/@floating-ui/dom@1.5.4:
|
||||
resolution: {integrity: sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==}
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.5.3
|
||||
'@floating-ui/utils': 0.2.1
|
||||
dev: false
|
||||
|
||||
/@floating-ui/react-dom@2.0.5(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-UsBK30Bg+s6+nsgblXtZmwHhgS2vmbuQK22qgt2pTQM6M3X6H1+cQcLXqgRY3ihVLcZJE6IvqDQozhsnIVqK/Q==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.5.4
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@floating-ui/react@0.26.5(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-LJeSQa+yOwV0Tdpc/C3Vr92QMrwRqRMTk4yOwsRJKc57x3Lcw317GE0EV+ECM7+Z89yEAPBe7nzbDEWfkWCrBA==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
dependencies:
|
||||
'@floating-ui/react-dom': 2.0.5(react-dom@18.2.0)(react@18.2.0)
|
||||
'@floating-ui/utils': 0.2.1
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
tabbable: 6.2.0
|
||||
dev: false
|
||||
|
||||
/@floating-ui/utils@0.2.1:
|
||||
resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
|
||||
dev: false
|
||||
|
||||
/@headlessui/react@2.0.0-alpha.4(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-spykSTXskDUYjSFhdId97Bqclo1F9Ky2pgLmyNKdV4f7aRDncdc/mjMPx67eEWRN2xQobksaUCcnn5K/AcRXsg==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
react: ^16 || ^17 || ^18
|
||||
react-dom: ^16 || ^17 || ^18
|
||||
dependencies:
|
||||
'@floating-ui/react': 0.26.5(react-dom@18.2.0)(react@18.2.0)
|
||||
'@react-aria/focus': 3.16.0(react@18.2.0)
|
||||
'@react-aria/interactions': 3.0.0-nightly.2584(react@18.2.0)
|
||||
'@tanstack/react-virtual': 3.0.0-beta.60(react@18.2.0)
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@humanwhocodes/config-array@0.11.13:
|
||||
resolution: {integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==}
|
||||
engines: {node: '>=10.10.0'}
|
||||
@ -1593,6 +1652,123 @@ packages:
|
||||
resolution: {integrity: sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==}
|
||||
dev: false
|
||||
|
||||
/@react-aria/focus@3.16.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-GP6EYI07E8NKQQcXHjpIocEU0vh0oi0Vcsd+/71fKS0NnTR0TUOEeil0JuuQ9ymkmPDTu51Aaaa4FxVsuN/23A==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-aria/interactions': 3.20.1(react@18.2.0)
|
||||
'@react-aria/utils': 3.23.0(react@18.2.0)
|
||||
'@react-types/shared': 3.22.0(react@18.2.0)
|
||||
'@swc/helpers': 0.5.2
|
||||
clsx: 2.1.0
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-aria/interactions@3.0.0-nightly.2584(react@18.2.0):
|
||||
resolution: {integrity: sha512-6DqYQx8XnbCfIen33uLz4kdgevrXLW6aoxsBOTY/Mzq9n0LHzbG/5H87obrOxRNVYh62RcQolo/qfqEpXZ7bVA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-aria/ssr': 3.9.1-nightly.4295(react@18.2.0)
|
||||
'@react-aria/utils': 3.0.0-nightly.2584(react@18.2.0)
|
||||
'@react-types/shared': 3.0.0-nightly.2584(react@18.2.0)
|
||||
'@swc/helpers': 0.5.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-aria/interactions@3.20.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-PLNBr87+SzRhe9PvvF9qvzYeP4ofTwfKSorwmO+hjr3qoczrSXf4LRQlb27wB6hF10C7ZE/XVbUI1lj4QQrZ/g==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-aria/ssr': 3.9.1(react@18.2.0)
|
||||
'@react-aria/utils': 3.23.0(react@18.2.0)
|
||||
'@react-types/shared': 3.22.0(react@18.2.0)
|
||||
'@swc/helpers': 0.5.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-aria/ssr@3.9.1(react@18.2.0):
|
||||
resolution: {integrity: sha512-NqzkLFP8ZVI4GSorS0AYljC13QW2sc8bDqJOkBvkAt3M8gbcAXJWVRGtZBCRscki9RZF+rNlnPdg0G0jYkhJcg==}
|
||||
engines: {node: '>= 12'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-aria/ssr@3.9.1-nightly.4295(react@18.2.0):
|
||||
resolution: {integrity: sha512-cv0+RaS3LJeZiSJ4pVGqSAyiyL+rieLiR3ctyoU7EwkArY1W7fI3NSkMEbNhHe4YoqqjPy1ZzAcpSA11EceiBg==}
|
||||
engines: {node: '>= 12'}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-aria/utils@3.0.0-nightly.2584(react@18.2.0):
|
||||
resolution: {integrity: sha512-A6NP3Yc9MMA+PiRBMTpMlx5plaiK7ejl3cppdkKiNPHtFmZrzxn6o9WHth4NToqIUkJRWHIrpTK8a/gBgVFPOg==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-aria/ssr': 3.9.1-nightly.4295(react@18.2.0)
|
||||
'@react-stately/utils': 3.0.0-nightly.2584(react@18.2.0)
|
||||
'@react-types/shared': 3.0.0-nightly.2584(react@18.2.0)
|
||||
'@swc/helpers': 0.5.2
|
||||
clsx: 1.2.1
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-aria/utils@3.23.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-fJA63/VU4iQNT8WUvrmll3kvToqMurD69CcgVmbQ56V7ZbvlzFi44E7BpnoaofScYLLtFWRjVdaHsohT6O/big==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@react-aria/ssr': 3.9.1(react@18.2.0)
|
||||
'@react-stately/utils': 3.9.0(react@18.2.0)
|
||||
'@react-types/shared': 3.22.0(react@18.2.0)
|
||||
'@swc/helpers': 0.5.2
|
||||
clsx: 2.1.0
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-stately/utils@3.0.0-nightly.2584(react@18.2.0):
|
||||
resolution: {integrity: sha512-UOW2P+H3O7goB1mNEIwUdxr28CVHrKKvi+N1CQ0TGDwr+Bp6oIZK2aXE6aQluzgwZ36aRvLPW5dAoovpzTTcQQ==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-stately/utils@3.9.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-yPKFY1F88HxuZ15BG2qwAYxtpE4HnIU0Ofi4CuBE0xC6I8mwo4OQjDzi+DZjxQngM9D6AeTTD6F1V8gkozA0Gw==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
'@swc/helpers': 0.5.2
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-types/shared@3.0.0-nightly.2584(react@18.2.0):
|
||||
resolution: {integrity: sha512-SVqvg7B3rtzN1ypQni5g6sfpUNf4wODRDtiOalBFSJ02YuaUIr7gXVjafPYIXOC1BkJbZtPun/Pv4mCwNHFNbA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@react-types/shared@3.22.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-yVOekZWbtSmmiThGEIARbBpnmUIuePFlLyctjvCbgJgGhz8JnEJOipLQ/a4anaWfzAgzSceQP8j/K+VOOePleA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@rushstack/eslint-patch@1.6.1:
|
||||
resolution: {integrity: sha512-UY+FGM/2jjMkzQLn8pxcHGMaVLh9aEitG3zY2CiY7XHdLiz3bZOwa6oDxNqEMv7zZkV+cj5DOdz0cQ1BP5Hjgw==}
|
||||
dev: false
|
||||
@ -2127,6 +2303,19 @@ packages:
|
||||
tailwindcss: 3.4.1
|
||||
dev: false
|
||||
|
||||
/@tanstack/react-virtual@3.0.0-beta.60(react@18.2.0):
|
||||
resolution: {integrity: sha512-F0wL9+byp7lf/tH6U5LW0ZjBqs+hrMXJrj5xcIGcklI0pggvjzMNW9DdIBcyltPNr6hmHQ0wt8FDGe1n1ZAThA==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||
dependencies:
|
||||
'@tanstack/virtual-core': 3.0.0-beta.60
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@tanstack/virtual-core@3.0.0-beta.60:
|
||||
resolution: {integrity: sha512-QlCdhsV1+JIf0c0U6ge6SQmpwsyAT0oQaOSZk50AtEeAyQl9tQrd6qCHAslxQpgphrfe945abvKG8uYvw3hIGA==}
|
||||
dev: false
|
||||
|
||||
/@testing-library/dom@9.3.3:
|
||||
resolution: {integrity: sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==}
|
||||
engines: {node: '>=14'}
|
||||
@ -2996,6 +3185,11 @@ packages:
|
||||
wrap-ansi: 7.0.0
|
||||
dev: false
|
||||
|
||||
/clsx@1.2.1:
|
||||
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
/clsx@2.1.0:
|
||||
resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==}
|
||||
engines: {node: '>=6'}
|
||||
@ -6305,6 +6499,10 @@ packages:
|
||||
resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
|
||||
dev: false
|
||||
|
||||
/tabbable@6.2.0:
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||
dev: false
|
||||
|
||||
/tailwindcss@3.4.1:
|
||||
resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
10
src/admin/AdminPhotoMenu.tsx
Normal file
10
src/admin/AdminPhotoMenu.tsx
Normal file
@ -0,0 +1,10 @@
|
||||
import { authCached } from '@/cache';
|
||||
import AdminPhotoMenuClient, { AdminPhotoMenuClientProps }
|
||||
from './AdminPhotoMenuClient';
|
||||
|
||||
export default async function AdminPhotoMenu(props: AdminPhotoMenuClientProps) {
|
||||
const session = await authCached();
|
||||
return Boolean(session?.user?.email)
|
||||
? <AdminPhotoMenuClient {...props} />
|
||||
: null;
|
||||
}
|
||||
52
src/admin/AdminPhotoMenuClient.tsx
Normal file
52
src/admin/AdminPhotoMenuClient.tsx
Normal file
@ -0,0 +1,52 @@
|
||||
'use client';
|
||||
|
||||
import { pathForAdminPhotoEdit } from '@/site/paths';
|
||||
import { Menu } from '@headlessui/react';
|
||||
import clsx from 'clsx/lite';
|
||||
import Link from 'next/link';
|
||||
import { FiMoreHorizontal } from 'react-icons/fi';
|
||||
|
||||
export interface AdminPhotoMenuClientProps {
|
||||
photoId: string
|
||||
className?: string
|
||||
buttonClassName?: string
|
||||
}
|
||||
|
||||
export default function AdminPhotoMenuClient({
|
||||
photoId,
|
||||
className,
|
||||
buttonClassName,
|
||||
}: AdminPhotoMenuClientProps) {
|
||||
return (
|
||||
<div className={clsx(
|
||||
className,
|
||||
'relative',
|
||||
)}>
|
||||
<Menu>
|
||||
<Menu.Button className={clsx(
|
||||
buttonClassName,
|
||||
'p-1 py-1 min-h-0 border-none shadow-none',
|
||||
'text-dim',
|
||||
)}
|
||||
>
|
||||
<FiMoreHorizontal size={16} />
|
||||
</Menu.Button>
|
||||
<Menu.Items className={clsx(
|
||||
'absolute top-6 right-1',
|
||||
'text-sm',
|
||||
'px-3 py-1.5 rounded-md border',
|
||||
'bg-content',
|
||||
)}>
|
||||
<Menu.Item>
|
||||
<Link
|
||||
className="whitespace-nowrap"
|
||||
href={pathForAdminPhotoEdit(photoId)}
|
||||
>
|
||||
Edit Photo
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -26,7 +26,7 @@ export default function Badge({
|
||||
'px-[0.3rem] py-1 rounded-[0.25rem]',
|
||||
'text-[0.7rem] font-medium',
|
||||
highContrast
|
||||
? 'text-invert bg-main'
|
||||
? 'text-invert bg-primary'
|
||||
: 'text-medium bg-gray-300/30 dark:bg-gray-700/50',
|
||||
interactive && highContrast
|
||||
? 'hover:opacity-70'
|
||||
|
||||
@ -10,6 +10,8 @@ import PhotoCamera from '../camera/PhotoCamera';
|
||||
import { cameraFromPhoto } from '@/camera';
|
||||
import PhotoFilmSimulation from '@/simulation/PhotoFilmSimulation';
|
||||
import { sortTags } from '@/tag';
|
||||
import AdminPhotoMenu from '@/admin/AdminPhotoMenu';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
export default function PhotoLarge({
|
||||
photo,
|
||||
@ -72,12 +74,22 @@ export default function PhotoLarge({
|
||||
)}>
|
||||
{renderMiniGrid(<>
|
||||
<div className="-space-y-0.5">
|
||||
<div className="relative flex gap-1 items-start">
|
||||
<div className="flex-grow">
|
||||
<Link
|
||||
href={pathForPhoto(photo)}
|
||||
className="font-bold uppercase"
|
||||
>
|
||||
{titleForPhoto(photo)}
|
||||
</Link>
|
||||
</div>
|
||||
<Suspense>
|
||||
<AdminPhotoMenu
|
||||
photoId={photo.id}
|
||||
buttonClassName="translate-y-[-3px]"
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
{tags.length > 0 &&
|
||||
<PhotoTags tags={tags} />}
|
||||
</div>
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
hover:text-gray-600
|
||||
hover:dark:text-gray-400
|
||||
}
|
||||
/* Common Utilities */
|
||||
/* Common Utilities: Text */
|
||||
.text-main {
|
||||
@apply
|
||||
text-gray-900 dark:text-gray-100
|
||||
@ -141,7 +141,13 @@
|
||||
@apply
|
||||
text-red-500 dark:text-red-400
|
||||
}
|
||||
.bg-main {
|
||||
/* Common Utilities: Background */
|
||||
.bg-content {
|
||||
@apply
|
||||
bg-white border-gray-200
|
||||
dark:bg-black dark:border-gray-800
|
||||
}
|
||||
.bg-primary {
|
||||
@apply
|
||||
bg-gray-900 dark:bg-gray-100
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user