add treasures (artworks)

This commit is contained in:
Khairul Hidayat 2024-01-10 22:24:12 +07:00
parent 205f92001c
commit 2cbaca844e
22 changed files with 717 additions and 55 deletions

5
backend/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
CHANGELOG.md
LICENSE.md
*.zip
pocketbase
pb_data/

1
backend/.pbversion Normal file
View File

@ -0,0 +1 @@
0.20.5

13
backend/package.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "./pocketbase serve",
"clean-migrations": "rm -rf pb_migrations && ./pocketbase migrate collections"
},
"keywords": [],
"author": "",
"license": "ISC"
}

View File

@ -0,0 +1,11 @@
/* eslint-disable */
/// <reference path="../pb_data/types.d.ts" />
// artwork view hook
onRecordViewRequest((e) => {
const { record } = e;
if (record) {
record.set("views", record.getInt("views") + 1);
$app.dao().saveRecord(record);
}
}, "artworks");

View File

@ -0,0 +1,161 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const snapshot = [
{
"id": "_pb_users_auth_",
"created": "2024-01-10 09:29:21.510Z",
"updated": "2024-01-10 09:29:21.511Z",
"name": "users",
"type": "auth",
"system": false,
"schema": [
{
"system": false,
"id": "users_name",
"name": "name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "users_avatar",
"name": "avatar",
"type": "file",
"required": false,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [
"image/jpeg",
"image/png",
"image/svg+xml",
"image/gif",
"image/webp"
],
"thumbs": null,
"maxSelect": 1,
"maxSize": 5242880,
"protected": false
}
}
],
"indexes": [],
"listRule": "id = @request.auth.id",
"viewRule": "id = @request.auth.id",
"createRule": "",
"updateRule": "id = @request.auth.id",
"deleteRule": "id = @request.auth.id",
"options": {
"allowEmailAuth": true,
"allowOAuth2Auth": true,
"allowUsernameAuth": true,
"exceptEmailDomains": null,
"manageRule": null,
"minPasswordLength": 8,
"onlyEmailDomains": null,
"onlyVerified": false,
"requireEmail": false
}
},
{
"id": "eo6iaxf4pkeqynf",
"created": "2024-01-10 09:34:57.731Z",
"updated": "2024-01-10 15:08:00.292Z",
"name": "artworks",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "p6dor6eo",
"name": "image",
"type": "file",
"required": true,
"presentable": false,
"unique": false,
"options": {
"mimeTypes": [
"image/png",
"image/jpeg",
"image/gif",
"image/webp",
"image/tiff",
"image/bmp",
"image/svg+xml"
],
"thumbs": [
"256x192",
"256x384",
"32x48"
],
"maxSelect": 1,
"maxSize": 5242880,
"protected": false
}
},
{
"system": false,
"id": "9w1tjysa",
"name": "artistName",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "lkiiiwrt",
"name": "srcUrl",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "sfh7xwdb",
"name": "views",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": true
}
}
],
"indexes": [],
"listRule": "",
"viewRule": "",
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
}
];
const collections = snapshot.map((item) => new Collection(item));
return Dao(db).importCollections(collections, true, null);
}, (db) => {
return null;
})

View File

@ -17,9 +17,11 @@
"howler": "^2.2.4",
"lucide-react": "^0.306.0",
"pixi.js": "^7.3.3",
"pocketbase": "^0.20.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-helmet": "^6.1.0",
"react-query": "^3.39.3",
"react-router-dom": "^6.21.1",
"react-toastify": "^9.1.3",
"tailwind-merge": "^2.2.0",

View File

@ -1,11 +1,17 @@
import { ToastContainer } from "react-toastify";
import Router from "./Router";
import "react-toastify/dist/ReactToastify.css";
import { QueryClient, QueryClientProvider } from "react-query";
import { useState } from "react";
const App = () => {
const [queryClient] = useState(new QueryClient());
return (
<>
<Router />
<QueryClientProvider client={queryClient}>
<Router />
</QueryClientProvider>
<ToastContainer />
</>
);

View File

@ -5,6 +5,7 @@ import ErrorBoundaryPage from "./pages/errors/error-boundary/page";
const HomePage = lazy(() => import("./pages/home/page"));
const MyFurinaPage = lazy(() => import("./pages/my-furina/page"));
const ArtworksPage = lazy(() => import("./pages/artworks/page"));
const router = createBrowserRouter([
{
@ -12,6 +13,10 @@ const router = createBrowserRouter([
children: [
{ index: true, Component: HomePage },
{ path: "/toodle", Component: MyFurinaPage },
{
path: "/treasures",
children: [{ index: true, Component: ArtworksPage }],
},
],
ErrorBoundary: () => (
<MainLayout>

Binary file not shown.

View File

@ -9,7 +9,7 @@ import { cn } from "@/utility/utils";
const AppBar = () => {
return (
<header className="w-full bg-[#111a21] shadow">
<div className="container py-8 md:py-4 flex flex-col md:flex-row items-center gap-4">
<div className="mx-auto max-w-5xl md:px-4 pt-6 md:py-4 flex flex-col md:flex-row items-center gap-4">
<Link
to="/"
className="flex flex-col md:flex-row items-center gap-2 md:gap-4"
@ -24,6 +24,7 @@ const AppBar = () => {
<Navbar>
<NavbarItem path="/" title="Pet the Furina" />
<NavbarItem path="/treasures" title="Treasures‧₊˚" />
<NavbarItem path="/toodle" title="Toodle-oo~" />
</Navbar>
</div>
@ -33,7 +34,7 @@ const AppBar = () => {
const Navbar = ({ children }: ComponentProps<"div">) => {
return (
<nav className="flex-1 flex items-center justify-end gap-3 md:gap-5 overflow-x-auto md:overflow-x-hidden">
<nav className="md:flex-1 self-stretch md:self-center flex items-center px-2 md:px-0 md:justify-end md:gap-5 overflow-x-auto md:overflow-x-hidden">
{children}
</nav>
);
@ -50,7 +51,10 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
const isActive = isExact ? pathname === path : pathname.startsWith(path);
return (
<Link to={path} className="group flex items-center md:py-4">
<Link
to={path}
className="group flex flex-shrink-0 items-center px-2 md:px-0 md:py-4 first:ml-auto last:mr-auto"
>
<img
src={ahogeImg}
alt="ahoge"
@ -62,8 +66,8 @@ const NavbarItem = ({ path, title, isExact = true }: NavbarItemProps) => {
<p
className={cn(
"text-white ml-2 md:ml-4 md:text-xl border-b-2 border-dotted group-hover:border-white/80 border-transparent transition-all",
isActive ? "border-white" : ""
"text-white ml-2 md:ml-4 md:text-xl py-2 md:py-0 border-b-2 md:border-dotted group-hover:border-primary-500 border-transparent transition-all",
isActive ? "border-primary-500 md:border-white" : ""
)}
>
{title}

View File

@ -17,11 +17,8 @@ const MainLayout = ({ children }: ComponentProps<"div">) => {
</Suspense>
)}
<footer className="bg-primary-700 px-4 py-8 text-center">
<footer className="bg-primary-700 shadow relative z-10 px-4 py-8 text-center">
<p className="text-sm text-white">
<a href="http://rul.sh/">
Made with <span className="text-red-300"></span> by Khairul.
</a>{" "}
Artworks displayed belong to respective owners.
</p>
</footer>

View File

@ -0,0 +1,36 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/utility/utils";
const badgeVariants = cva(
"inline-flex items-center rounded border border-primary-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-primary-950 focus:ring-offset-2 dark:border-primary-800 dark:focus:ring-primary-300",
{
variants: {
variant: {
default:
"border-transparent bg-primary-600 text-primary-50 hover:bg-primary-600/80 dark:bg-primary-50 dark:text-primary-900 dark:hover:bg-primary-50/80",
secondary:
"border-transparent bg-primary-100 text-primary-900 hover:bg-primary-100/80 dark:bg-primary-800 dark:text-primary-50 dark:hover:bg-primary-800/80",
destructive:
"border-transparent bg-red-500 text-primary-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-primary-50 dark:hover:bg-red-900/80",
outline: "text-primary-950 dark:text-primary-50",
},
},
defaultVariants: {
variant: "default",
},
}
);
export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> {}
function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
);
}
export default Badge;

View File

@ -0,0 +1,42 @@
import { cn } from "@/utility/utils";
import React, { useState } from "react";
type Props = React.ComponentProps<"img"> & {
lazySrc: string;
containerClassName?: string;
};
const LazyImage = ({
lazySrc,
src,
containerClassName,
className,
...props
}: Props) => {
const [isLoaded, setLoaded] = useState(false);
return (
<div
className={cn(
"bg-no-repeat bg-cover",
!isLoaded ? "blur-md" : "",
containerClassName
)}
style={{ backgroundImage: `url('${lazySrc}')` }}
>
<img
src={src}
loading="lazy"
onLoad={() => setLoaded(true)}
className={cn(
"transition-all",
isLoaded ? "opacity-100" : "opacity-0",
className
)}
{...props}
/>
</div>
);
};
export default LazyImage;

156
src/components/ui/Sheet.tsx Normal file
View File

@ -0,0 +1,156 @@
import * as React from "react";
import * as SheetPrimitive from "@radix-ui/react-dialog";
import { cva, type VariantProps } from "class-variance-authority";
import { X } from "lucide-react";
import { cn } from "@/utility/utils";
const SheetRoot = SheetPrimitive.Root;
const SheetPortal = SheetPrimitive.Portal;
const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-black/20 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref}
/>
));
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName;
const sheetVariants = cva(
"fixed z-50 gap-4 bg-white shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500 dark:bg-slate-950",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
}
);
interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}
const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:ring-offset-slate-950 dark:focus:ring-slate-300 dark:data-[state=open]:bg-slate-800">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col p-4 space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
);
SheetHeader.displayName = "SheetHeader";
const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
);
SheetFooter.displayName = "SheetFooter";
const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn(
"text-lg font-semibold text-slate-950 dark:text-slate-50",
className
)}
{...props}
/>
));
SheetTitle.displayName = SheetPrimitive.Title.displayName;
const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-sm text-slate-500 dark:text-slate-400", className)}
{...props}
/>
));
SheetDescription.displayName = SheetPrimitive.Description.displayName;
type SheetProps = React.ComponentProps<typeof SheetRoot> & {
isOpen?: boolean;
title?: string;
description?: string;
position?: SheetContentProps["side"];
};
const Sheet = ({
isOpen,
title,
description,
children,
position,
...props
}: SheetProps) => {
return (
<SheetRoot open={isOpen} {...props}>
<SheetContent side={position} className="rounded-t-2xl">
<SheetHeader>
{title ? <SheetTitle>{title}</SheetTitle> : null}
{description ? (
<SheetDescription>{description}</SheetDescription>
) : null}
</SheetHeader>
<div className="max-h-[90vh] overflow-y-auto">{children}</div>
</SheetContent>
</SheetRoot>
);
};
export default Sheet;

View File

@ -1,12 +1,15 @@
import { useState } from "react";
const useModal = () => {
const useModal = <T = unknown>() => {
const [isOpen, setOpen] = useState(false);
const [data, setData] = useState<T | undefined | null>(null);
return {
isOpen,
onOpen() {
data,
onOpen(_data?: T | null) {
setOpen(true);
setData(_data);
},
onClose() {
setOpen(false);

View File

@ -0,0 +1,71 @@
import pb from "@/utility/api";
import { Howl } from "howler";
import { useQuery } from "react-query";
import { Link } from "react-router-dom";
import playIcon from "@/assets/icons/play-outline.svg";
import openingSfx from "@/assets/audio/VO_JA_Furina_Opening_Treasure_Chest_02.ogg";
import ViewSheet from "./viewSheet";
import useModal from "@/hooks/useModal";
import LazyImage from "@/components/ui/LazyImage";
const openingChestSfx = new Howl({
src: openingSfx,
preload: true,
});
const ArtworksPage = () => {
const { data } = useQuery({
queryKey: ["artworks"],
queryFn: () =>
pb.collection("artworks").getList(1, 100, { sort: "-created" }),
});
const viewItemModal = useModal<string>();
return (
<div className="container py-16">
<h1 className="text-2xl">Treasures</h1>
<div>
<p className="italic inline">Take it. Ahem... I allow you!</p>
<button
type="button"
className="bg-white rounded border-2 border-primary-300 hover:bg-gray-200 p-1 md:p-0.5 ml-2"
>
<img
src={playIcon}
alt="play"
className="h-4"
onClick={() => openingChestSfx.play()}
/>
</button>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4 mt-8">
{data?.items.map((item) => (
<Link
key={item.id}
// to={`/treasures/${item.id}`}
to="#"
className="bg-white rounded-lg shadow border border-gray-300 overflow-hidden hover:scale-105 transition-all relative"
onClick={(e) => {
e.preventDefault();
viewItemModal.onOpen(item.id);
}}
>
<LazyImage
lazySrc={pb.files.getUrl(item, item.image, { thumb: "32x48" })}
src={pb.files.getUrl(item, item.image)}
className="w-full aspect-[0.8] object-cover"
/>
<div className="absolute bottom-2 left-2 px-3 py-1 rounded-md bg-black/20 backdrop-blur-sm">
<p className="text-white">{item.artistName}</p>
</div>
</Link>
))}
</div>
<ViewSheet modal={viewItemModal} />
</div>
);
};
export default ArtworksPage;

View File

@ -0,0 +1,75 @@
import Sheet from "@/components/ui/Sheet";
import useModal from "@/hooks/useModal";
import pb from "@/utility/api";
import { useQuery } from "react-query";
import loadingIllust from "@/assets/images/l9fsdoa2j7vb1.gif";
import Badge from "@/components/ui/Badge";
import Button from "@/components/ui/Button";
import { ChevronLeft } from "lucide-react";
type Props = {
modal: ReturnType<typeof useModal<string>>;
};
const ViewSheet = ({ modal }: Props) => {
const id = modal.data;
const { data, isLoading, isError } = useQuery({
queryKey: ["artwork", id],
queryFn: () => pb.collection("artworks").getOne(id || ""),
enabled: !!id,
});
return (
<Sheet {...modal} title="View Item" position="bottom">
{isLoading ? (
<div className="min-h-[320px] flex flex-col items-center justify-center text-center">
<img src={loadingIllust} className="h-40 animate-bounce" />
<p className="mt-2">Please wait a moment...</p>
</div>
) : isError || !data ? (
<div className="min-h-[320px] flex flex-col items-center justify-center text-center">
<h1 className="text-2xl">An error occured.</h1>
<p className="mt-2">Cannot load item</p>
</div>
) : (
<div className="flex flex-col md:flex-row">
<div className="flex-1 bg-gray-50">
<a href={data.srcUrl} target="_blank">
<img
src={pb.files.getUrl(data, data.image)}
className="w-full max-h-[80vh] object-contain"
/>
</a>
</div>
<div className="md:w-1/3 border-t md:border-l md:border-t-0 py-4 md:pt-0 pl-4 lg:pl-8 overflow-y-auto overflow-x-hidden truncate">
<Button
className="hidden md:flex pl-2 md:mb-6"
onClick={modal.onClose}
>
<ChevronLeft /> Back
</Button>
<Badge>Artist Name</Badge>
<p className="mt-1 truncate">{data.artistName}</p>
<Badge className="mt-4">Source</Badge>
<a
href={data.srcUrl}
target="_blank"
className="block mt-1 text-primary-500 font-medium hover:underline truncate"
>
{cleanUrl(data.srcUrl)}
</a>
</div>
</div>
)}
</Sheet>
);
};
const cleanUrl = (url: string) => {
return url.replace("https://", "").replace("http://", "").replace("www.", "");
};
export default ViewSheet;

View File

@ -65,7 +65,7 @@ const Credits = () => {
const modal = useModal();
return (
<div className="container pt-4 pb-16 border-t">
<div className="container pt-4 pb-16">
<Button onClick={modal.onOpen}>Assets Credits</Button>
<Modal {...modal} title="Big Thanks to:" size="xl">

View File

@ -4,13 +4,18 @@ import buildImg from "@/assets/images/furina-build.webp";
import furinaChibiImg from "@/assets/images/113932900_p0_master1200.webp";
import copyIcon from "@/assets/icons/copy-outline.svg";
import playIcon from "@/assets/icons/play-outline.svg";
import skillAudio from "@/assets/audio/VO_JA_Furina_Elemental_Skill_1_04.ogg";
import skillAudio from "@/assets/audio/VO_JA_Furina_Elemental_Burst_03.ogg";
import { cn, copyToClipboard, showToast } from "@/utility/utils";
import { useEffect, useRef, useState } from "react";
import { useEffect, useState } from "react";
import { Howl } from "howler";
import styles from "./style.module.css";
import PageMetadata from "@/components/containers/PageMetadata";
const gameUID = "828243224";
const skillAudioSfx = new Howl({
src: skillAudio,
preload: true,
});
const FurinaBelovedPage = () => {
const onCopyUID = () => {
@ -132,47 +137,22 @@ const HeroBackground = () => {
};
const SkillAudioPlayer = () => {
const skillAudioRef = useRef<HTMLAudioElement>(null);
const playingTimeout = useRef<any>(null);
const [isPlaying, setPlaying] = useState(false);
const onPlayAudio = () => {
skillAudioRef.current?.play();
setPlaying(true);
if (playingTimeout.current) {
clearTimeout(playingTimeout.current);
}
playingTimeout.current = setTimeout(() => {
setPlaying(false);
playingTimeout.current = null;
}, 4000);
};
return (
<>
<button
type="button"
className={`group flex flex-col items-center justify-center text-center border border-transparent hover:border-primary-500
<button
type="button"
className={`group flex flex-col items-center justify-center text-center border border-transparent hover:border-primary-500
rounded-xl px-6 py-4 hover:shadow transition-all hover:text-orange-500 hover:scale-105 active:scale-100 active:translate-y-5`}
onClick={onPlayAudio}
>
<img
src={playIcon}
className={cn("h-8", isPlaying ? styles.playAnims : "")}
/>
<p className="text-xl font-light mt-4">
{" "}
<span className="group-hover:text-purple-500">The curtain rises</span>{" "}
˚
</p>
</button>
<audio ref={skillAudioRef}>
<source src={skillAudio} type="audio/ogg" />
</audio>
</>
onClick={() => skillAudioSfx.play()}
>
<img src={playIcon} className="h-8" />
<p className="text-xl font-light mt-4">
{" "}
<span className="group-hover:text-purple-500">
Let my name echo in song!
</span>{" "}
˚
</p>
</button>
);
};

6
src/utility/api.ts Normal file
View File

@ -0,0 +1,6 @@
import PocketBase from "pocketbase";
export const baseUrl = "http://127.0.0.1:8090";
const pb = new PocketBase(baseUrl);
export default pb;

View File

@ -184,6 +184,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2":
version "7.23.8"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650"
integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.13.10", "@babel/runtime@^7.23.5":
version "7.23.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.7.tgz#dd7c88deeb218a0f8bd34d5db1aa242e0f203193"
@ -1209,6 +1216,11 @@ balanced-match@^1.0.0:
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
big-integer@^1.6.16:
version "1.6.52"
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85"
integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
@ -1236,6 +1248,20 @@ braces@^3.0.2, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
broadcast-channel@^3.4.1:
version "3.7.0"
resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
dependencies:
"@babel/runtime" "^7.7.2"
detect-node "^2.1.0"
js-sha3 "0.8.0"
microseconds "0.2.0"
nano-time "1.0.0"
oblivious-set "1.0.0"
rimraf "3.0.2"
unload "2.2.0"
browserslist@^4.21.10, browserslist@^4.22.2:
version "4.22.2"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b"
@ -1408,6 +1434,11 @@ detect-node-es@^1.1.0:
resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==
detect-node@^2.0.4, detect-node@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
didyoumean@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
@ -1938,6 +1969,11 @@ jiti@^1.19.1:
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d"
integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==
js-sha3@0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@ -2048,6 +2084,14 @@ lucide-react@^0.306.0:
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.306.0.tgz#07e427c1c0e6c6d50712b746b889ff807606131c"
integrity sha512-eShuk2PI3vxN4YN8kNPmhAsroSvPXbtaxU/UX/zrBcLLg8FeFH9MG7C2EYzYsT2rNrPVjbP7rpBz3mOviZYN3A==
match-sorter@^6.0.2:
version "6.3.1"
resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
dependencies:
"@babel/runtime" "^7.12.5"
remove-accents "0.4.2"
merge2@^1.3.0, merge2@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
@ -2061,6 +2105,11 @@ micromatch@^4.0.4, micromatch@^4.0.5:
braces "^3.0.2"
picomatch "^2.3.1"
microseconds@0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
minimatch@9.0.3, minimatch@^9.0.1:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
@ -2094,6 +2143,13 @@ mz@^2.7.0:
object-assign "^4.0.1"
thenify-all "^1.0.0"
nano-time@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==
dependencies:
big-integer "^1.6.16"
nanoid@^3.3.7:
version "3.3.7"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8"
@ -2134,6 +2190,11 @@ object-inspect@^1.9.0:
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
oblivious-set@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@ -2263,6 +2324,11 @@ pixi.js@^7.3.3:
"@pixi/text-bitmap" "7.3.3"
"@pixi/text-html" "7.3.3"
pocketbase@^0.20.1:
version "0.20.1"
resolved "https://registry.yarnpkg.com/pocketbase/-/pocketbase-0.20.1.tgz#e34a0113306322f9382a8b775de3b83f7304929f"
integrity sha512-Gl51UBc1U03JlwmwMkUIa1OHbcTmmdhyMPV1aJyHp9HuY5VUlh0t4hcx6D1fdhYsJcoh3kc6mpwhTBfXDoyn8w==
postcss-import@^15.1.0:
version "15.1.0"
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-15.1.0.tgz#41c64ed8cc0e23735a9698b3249ffdbf704adc70"
@ -2380,6 +2446,15 @@ react-is@^16.13.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
react-query@^3.39.3:
version "3.39.3"
resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.3.tgz#4cea7127c6c26bdea2de5fb63e51044330b03f35"
integrity sha512-nLfLz7GiohKTJDuT4us4X3h/8unOh+00MLb2yJoGTPjxKs2bc1iDhkNx2bd5MKklXnOD3NrVZ+J2UXujA5In4g==
dependencies:
"@babel/runtime" "^7.5.5"
broadcast-channel "^3.4.1"
match-sorter "^6.0.2"
react-refresh@^0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
@ -2466,6 +2541,11 @@ regenerator-runtime@^0.14.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f"
integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==
remove-accents@0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
@ -2485,7 +2565,7 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
rimraf@^3.0.2:
rimraf@3.0.2, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@ -2764,6 +2844,14 @@ undici-types@~5.26.4:
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617"
integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==
unload@2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
dependencies:
"@babel/runtime" "^7.6.2"
detect-node "^2.0.4"
update-browserslist-db@^1.0.13:
version "1.0.13"
resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4"