vercel json add

This commit is contained in:
Artur Agh
2024-01-23 08:16:57 +01:00
parent 977b750476
commit 6cbc874042
19 changed files with 552 additions and 47 deletions

View File

@@ -42,6 +42,7 @@
"@trpc/next": "^10.37.1",
"@trpc/react-query": "^10.37.1",
"@trpc/server": "^10.37.1",
"@types/react-color": "^3.0.10",
"@types/uuid": "^9.0.3",
"@uiw/react-color": "^2.0.3",
"axios": "^1.5.1",
@@ -55,6 +56,7 @@
"next": "^13.4.19",
"next-auth": "^4.23.0",
"react": "18.2.0",
"react-color": "^2.19.3",
"react-dom": "18.2.0",
"react-hook-form": "^7.49.0",
"react-icons": "^4.11.0",

68
pnpm-lock.yaml generated
View File

@@ -98,6 +98,9 @@ dependencies:
'@trpc/server':
specifier: ^10.37.1
version: 10.42.0
'@types/react-color':
specifier: ^3.0.10
version: 3.0.10
'@types/uuid':
specifier: ^9.0.3
version: 9.0.6
@@ -137,6 +140,9 @@ dependencies:
react:
specifier: 18.2.0
version: 18.2.0
react-color:
specifier: ^2.19.3
version: 2.19.3(react@18.2.0)
react-dom:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
@@ -386,6 +392,14 @@ packages:
resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
dev: true
/@icons/material@0.2.4(react@18.2.0):
resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==}
peerDependencies:
react: '*'
dependencies:
react: 18.2.0
dev: false
/@jridgewell/gen-mapping@0.3.3:
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
engines: {node: '>=6.0.0'}
@@ -2083,6 +2097,13 @@ packages:
/@types/prop-types@15.7.9:
resolution: {integrity: sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==}
/@types/react-color@3.0.10:
resolution: {integrity: sha512-6K5BAn3zyd8lW8UbckIAVeXGxR82Za9jyGD2DBEynsa7fKaguLDVtjfypzs7fgEV7bULgs7uhds8A8v1wABTvQ==}
dependencies:
'@types/react': 18.2.31
'@types/reactcss': 1.2.11
dev: false
/@types/react-dom@18.2.14:
resolution: {integrity: sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==}
dependencies:
@@ -2101,6 +2122,12 @@ packages:
'@types/scheduler': 0.16.5
csstype: 3.1.2
/@types/reactcss@1.2.11:
resolution: {integrity: sha512-0fFy0ubuPlhksId8r9V8nsLcxBAPQnn15g/ERAElgE9L6rOquMj2CapsxqfyBuHlkp0/ndEUVnkYI7MkTtkGpw==}
dependencies:
'@types/react': 18.2.31
dev: false
/@types/scheduler@0.16.5:
resolution: {integrity: sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==}
@@ -4034,10 +4061,18 @@ packages:
p-locate: 5.0.0
dev: true
/lodash-es@4.17.21:
resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
dev: false
/lodash.merge@4.6.2:
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
dev: true
/lodash@4.17.21:
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
dev: false
/loose-envify@1.4.0:
resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
hasBin: true
@@ -4058,6 +4093,10 @@ packages:
react: 18.2.0
dev: false
/material-colors@1.2.6:
resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==}
dev: false
/merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -4519,7 +4558,6 @@ packages:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
dev: true
/proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
@@ -4533,6 +4571,21 @@ packages:
/queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
/react-color@2.19.3(react@18.2.0):
resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==}
peerDependencies:
react: '*'
dependencies:
'@icons/material': 0.2.4(react@18.2.0)
lodash: 4.17.21
lodash-es: 4.17.21
material-colors: 1.2.6
prop-types: 15.8.1
react: 18.2.0
reactcss: 1.2.3(react@18.2.0)
tinycolor2: 1.6.0
dev: false
/react-dom@18.2.0(react@18.2.0):
resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
peerDependencies:
@@ -4728,6 +4781,15 @@ packages:
loose-envify: 1.4.0
dev: false
/reactcss@1.2.3(react@18.2.0):
resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==}
peerDependencies:
react: '*'
dependencies:
lodash: 4.17.21
react: 18.2.0
dev: false
/read-cache@1.0.0:
resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
dependencies:
@@ -5089,6 +5151,10 @@ packages:
dependencies:
any-promise: 1.3.0
/tinycolor2@1.6.0:
resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==}
dev: false
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View File

@@ -34,7 +34,7 @@ export default function LayerItem({ item }: { item: StageItem }) {
style={style}
ref={setNodeRef}
{...attributes}
className="flex items-center justify-between rounded-md border bg-yellow-50 p-2 text-black"
className="flex items-center justify-between rounded-md border p-2 text-black"
>
<IconButton {...listeners}>
<GripVertical size={18} />

View File

@@ -5,12 +5,9 @@ import {
PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { Card, CardHeader, CardTitle } from "@/components/ui/card";
import axios from "axios";
import * as process from "process";
import { LuLayoutTemplate } from "react-icons/lu";
import Image from "next/image";
import { Input } from "@/components/ui/input";
export const ImageGallery = () => {
const [open, setOpen] = useState<boolean>(false);

View File

@@ -11,12 +11,15 @@ import * as process from "process";
import Image from "next/image";
import { Input } from "@/components/ui/input";
import { addImage } from "@/store/app.slice";
import { models } from "@/components/layout/sidebar/stage-settings/stage-size-selector/models";
import { useAppDispatch } from "@/hooks";
type Props = {
open: boolean;
};
export const ImageGallery = ({ open }: Props) => {
const dispatch = useAppDispatch();
const [query, setQuery] = useState<string>("");
const [page, setPage] = useState<number>(1);
const [isLoading, setIsLoading] = useState<boolean>(false);
@@ -41,7 +44,6 @@ export const ImageGallery = ({ open }: Props) => {
if (query) {
url = `${searchUrl}${clientID}${urlPage}${urlQuery}`;
console.log(url);
} else {
url = `${mainUrl}${clientID}${urlPage}`;
}
@@ -55,7 +57,6 @@ export const ImageGallery = ({ open }: Props) => {
} else if (query) {
return [...oldPhotos, ...data.results];
} else {
console.log(data);
return [...oldPhotos, ...data];
}
});
@@ -70,10 +71,10 @@ export const ImageGallery = ({ open }: Props) => {
const ref = scrollAreaRef.current;
const handler = () => {
if (scrollAreaRef.current) {
console.log(
/* console.log(
scrollAreaRef.current.clientHeight + scrollAreaRef.current.scrollTop,
scrollAreaRef.current.scrollHeight - 2,
);
);*/
}
if (
isLoading ||
@@ -104,16 +105,17 @@ export const ImageGallery = ({ open }: Props) => {
return () => ref?.removeEventListener("scroll", handler);
}, [isLoading, open]);
const handleImageAdd = (e) => {
console.log(e.target);
const handleImageAdd = (url: string, width: number, height: number) => {
const aspectRatio = height / width;
const finalWidth = 200;
const finalHeight = finalWidth * aspectRatio;
dispatch(
addImage({ imageUrl: url, width: finalWidth, height: finalHeight }),
);
};
return (
<Card className="min-h-full p-3">
<CardHeader className="mb-2 p-2 text-center">
<CardTitle>Template</CardTitle>
</CardHeader>
<Card className="h-full min-h-[600px] p-3">
<Input
type="text"
placeholder="Search Images"
@@ -122,24 +124,27 @@ export const ImageGallery = ({ open }: Props) => {
/>
<div
className="mt-3 max-h-64 w-full grid-cols-2 justify-center overflow-auto"
className="mt-3 grid max-h-[550px] w-full grid-cols-2 justify-center gap-2 overflow-auto "
ref={scrollAreaRef}
>
{photos?.map((p, i) => (
<div
key={i}
onClick={(e) => handleImageAdd(e)}
className="rounded-md border-2 border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
>
<Image
key={p.i}
src={p.urls.small}
alt={"image"}
height={100}
width={100}
/>
</div>
))}
{photos?.map((p, i) => {
return (
<div
key={i}
onClick={() => handleImageAdd(p.urls.raw, p.width, p.height)}
className=" rounded-md border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
>
<Image
key={p.i}
src={p.urls.small}
alt={"image"}
height={100}
width={100}
className="h-32 w-full"
/>
</div>
);
})}
</div>
</Card>
);

View File

@@ -28,9 +28,6 @@ export const Layers = () => {
return (
<Card className="p-2">
<CardHeader>
<CardTitle className="text-center">Layers</CardTitle>
</CardHeader>
<CardContent className="grid items-center gap-2 p-2 ">
<DndContext
collisionDetection={closestCenter}

View File

@@ -6,6 +6,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import React, { useState } from "react";
import { LuLayoutTemplate } from "react-icons/lu";
import {
ColumnsIcon,
GearIcon,
ImageIcon,
LayersIcon,
@@ -13,6 +14,7 @@ import {
} from "@radix-ui/react-icons";
import { StageSize } from "@/components/layout/sidebar/stage-settings/stage-size";
import { StageBackground } from "@/components/layout/sidebar/stage-settings/stage-background";
import { StageBackgroundChange } from "@/components/layout/sidebar/stage-settings/stage-background-change/stage-background-change";
export function Sidebar() {
const [openGallery, setOpenGallery] = useState<boolean>(false);
@@ -20,7 +22,7 @@ export function Sidebar() {
return (
<div className="items-left flex w-full flex-col p-2 pt-4">
<Tabs>
<TabsList className="grid grid-cols-5">
<TabsList className="grid grid-cols-6">
<TabsTrigger value="stage">
<GearIcon />
</TabsTrigger>
@@ -41,6 +43,9 @@ export function Sidebar() {
<TabsTrigger value="layers">
<LayersIcon />
</TabsTrigger>
<TabsTrigger value="background">
<ColumnsIcon />
</TabsTrigger>
</TabsList>
<TabsContent value="stage">
<StageSize />
@@ -58,6 +63,9 @@ export function Sidebar() {
<TabsContent value="layers">
<Layers />
</TabsContent>
<TabsContent value="background">
<StageBackgroundChange />
</TabsContent>
</Tabs>
</div>
);

View File

@@ -0,0 +1,159 @@
import React, { type ElementRef, useEffect, useRef, useState } from "react";
import { Card } from "@/components/ui/card";
import axios from "axios";
import * as process from "process";
import Image from "next/image";
import { selectBackground, setBackgroundImage } from "@/store/app.slice";
import { useAppDispatch, useAppSelector } from "@/hooks";
import { SliderPicker } from "react-color";
type Props = {
open: boolean;
};
export const StageBackgroundChange = ({ open }: Props) => {
const dispatch = useAppDispatch();
const { stage } = useAppSelector((state) => state.app.history[0]);
const [query, setQuery] = useState<string>("gradient");
const [page, setPage] = useState<number>(1);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [photos, setPhotos] = useState<any[]>([]);
const scrollAreaRef = useRef<ElementRef<"div">>(null);
const mainUrl = "https://api.unsplash.com/photos";
const searchUrl = "https://api.unsplash.com/search/photos";
const clientID = `?client_id=${process.env.NEXT_PUBLIC_ACCESS_KEY}`;
const bgColor = useAppSelector((state) => state.app.backgroundColor);
useEffect(() => {
fetchImages();
}, [query, page]);
const fetchImages = async () => {
setIsLoading(true);
let url;
const urlPage = `&page=${page}`;
const urlQuery = `&query=${query}`;
if (query) {
url = `${searchUrl}${clientID}${urlPage}${urlQuery}`;
} else {
url = `${mainUrl}${clientID}${urlPage}`;
}
try {
const { data } = await axios.get(url);
setPhotos((oldPhotos) => {
if (query && page === 1) {
return data.results;
} else if (query) {
return [...oldPhotos, ...data.results];
} else {
return [...oldPhotos, ...data];
}
});
setIsLoading(false);
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
useEffect(() => {
const ref = scrollAreaRef.current;
const handler = () => {
if (scrollAreaRef.current) {
/*console.log(
scrollAreaRef.current.clientHeight + scrollAreaRef.current.scrollTop,
scrollAreaRef.current.scrollHeight - 2,
);*/
}
if (
isLoading ||
!scrollAreaRef.current ||
scrollAreaRef.current.clientHeight + scrollAreaRef.current.scrollTop <
scrollAreaRef.current.scrollHeight - 2
) {
return;
}
setPage((oldPage) => {
return oldPage + 1;
});
};
scrollAreaRef.current?.addEventListener("scroll", handler);
return () => ref?.removeEventListener("scroll", handler);
}, [isLoading, open]);
const handleImageAdd = (url: string) => {
dispatch(
setBackgroundImage({
imageUrl: url,
}),
);
};
const handleBackgroundSelect = (bgColor) => {
// dispatch(selectBackground(bgColor.hex));
console.log(bgColor);
};
return (
<Card className="h-full min-h-[600px] p-3">
{/*<Input
type="text"
placeholder="Search Images"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
*/}
<div className=" mt-4">
{/* <input
className="h-[2rem] w-[2rem]"
type="color"
value={bgColor ?? undefined}
onChange={(e) => handleBackgroundSelect(e.target.value)}
/>
<input
className="h-[2rem] w-[2rem]"
type="color"
value={bgColor ?? undefined}
onChange={(e) => handleBackgroundSelect(e.target.value)}
/>*/}
<SliderPicker
color={bgColor}
onChangeComplete={(e) => handleBackgroundSelect(e)}
/>
</div>
<div
className="grid max-h-[550px] w-full grid-cols-2 justify-center gap-2 overflow-auto "
ref={scrollAreaRef}
>
{photos?.map((p, i) => {
return (
<div
key={i}
onClick={() => handleImageAdd(p.urls.raw)}
className="rounded-md border-muted bg-popover p-2 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary"
>
<Image
key={p.i}
src={p.urls.small}
alt={"image"}
height={100}
width={100}
className="h-32 w-full"
/>
</div>
);
})}
</div>
</Card>
);
};

View File

@@ -0,0 +1,163 @@
"use client";
import * as React from "react";
import { CaretSortIcon, CheckIcon } from "@radix-ui/react-icons";
import { type PopoverProps } from "@radix-ui/react-popover";
import { cn } from "@/lib/utils";
import { useMutationObserver } from "@/hooks/use-mutation-observer";
import {
Command,
CommandGroup,
CommandItem,
CommandList,
} from "@/components/ui/command";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Label } from "@/components/ui/label";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { type Model, type ModelType } from "./models";
import { Button } from "@/components/ui/button";
import Image from "next/image";
interface ModelSelectorProps extends PopoverProps {
types: readonly ModelType[];
models: Model[];
}
export function ModelSelector({ models, types, ...props }: ModelSelectorProps) {
const [open, setOpen] = React.useState(false);
const [selectedModel, setSelectedModel] = React.useState<Model>(models[0]);
const [peekedModel, setPeekedModel] = React.useState<Model>(models[0]);
return (
<div className="grid gap-2">
<HoverCard openDelay={200}>
<HoverCardTrigger asChild>
<Label htmlFor="model">Selectionnez la taille d'etiquette</Label>
</HoverCardTrigger>
<HoverCardContent
align="start"
className="w-[260px] bg-yellow-400 text-sm"
side="left"
>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Accusantium
alias aut eaque eligendi fugiat harum, id ipsa ipsum maxime molestiae
nesciunt nobis odio quisquam quos reprehenderit sapiente suscipit, vel
voluptatibus.
</HoverCardContent>
</HoverCard>
<Popover open={open} onOpenChange={setOpen} {...props}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={open}
aria-label="Select a model"
className="w-full justify-between"
>
{selectedModel ? selectedModel.name : "Select a model..."}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-[250px] p-0">
<HoverCard>
<HoverCardContent
side="left"
align="start"
forceMount
className="min-h-[500px] min-w-[200px] "
>
<div className="grid gap-2">
<h4 className="font-medium leading-none">{peekedModel.name}</h4>
<div className="text-sm text-muted-foreground">
{peekedModel.description}
</div>
{peekedModel.image ? (
<div>
<Image
src={peekedModel.image}
alt={peekedModel.name}
width={200}
height={500}
/>
</div>
) : null}
</div>
</HoverCardContent>
<Command loop>
<CommandList className="h-[var(--cmdk-list-height)] max-h-[400px]">
<HoverCardTrigger />
{types.map((type) => (
<CommandGroup key={type} heading={type}>
{models
.filter((model) => model.type === type)
.map((model) => (
<ModelItem
key={model.id}
model={model}
isSelected={selectedModel?.id === model.id}
onPeek={(model) => setPeekedModel(model)}
onSelect={() => {
setSelectedModel(model);
setOpen(false);
}}
/>
))}
</CommandGroup>
))}
</CommandList>
</Command>
</HoverCard>
</PopoverContent>
</Popover>
</div>
);
}
interface ModelItemProps {
model: Model;
isSelected: boolean;
onSelect: () => void;
onPeek: (model: Model) => void;
}
function ModelItem({ model, isSelected, onSelect, onPeek }: ModelItemProps) {
const ref = React.useRef<HTMLDivElement>(null);
useMutationObserver(ref, (mutations) => {
for (const mutation of mutations) {
if (mutation.type === "attributes") {
if (mutation.attributeName === "aria-selected") {
onPeek(model);
}
}
}
});
return (
<CommandItem
key={model.id}
onSelect={onSelect}
ref={ref}
className="aria-selected:bg-primary aria-selected:text-primary-foreground"
>
{model.name}
<CheckIcon
className={cn(
"ml-auto h-4 w-4",
isSelected ? "opacity-100" : "opacity-0",
)}
/>
</CommandItem>
);
}

View File

@@ -0,0 +1,36 @@
export const types = ["GPT-3", "Codex"] as const;
export type ModelType = (typeof types)[number];
export interface Model<Type = string> {
id: string;
name: string;
description: string;
image: string;
type: Type;
}
export const models: Model<ModelType>[] = [
{
id: "1",
name: "Demie (S)",
description:
"Most capable GPT-3 model. Can do any task the other models can do, often with higher quality, longer output and better instruction-following. Also supports inserting completions within text.",
type: "GPT-3",
image: "/bottle-images/bouteille-demie.png",
},
{
id: "2",
name: "Bouteille (M)",
image: "/bottle-images/bouteille-standard.png",
description: "Very capable, but faster and lower cost than Davinci.",
type: "GPT-3",
},
{
id: "3",
name: "Magnum (L)",
image: "/bottle-images/bouteille-magnum.png",
description: "Capable of straightforward tasks, very fast, and lower cost.",
type: "GPT-3",
},
];

View File

@@ -3,6 +3,8 @@ import { Card, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { useAppDispatch } from "@/hooks";
import { updateStage } from "@/store/app.slice";
import { ModelSelector } from "@/components/layout/sidebar/stage-settings/stage-size-selector/model-selector";
import { models, types } from "./stage-size-selector/models";
export const StageSize = () => {
const dispatch = useAppDispatch();
@@ -15,7 +17,7 @@ export const StageSize = () => {
};
return (
<Card className="p-3">
<CardHeader>
{/*<CardHeader>
<CardTitle>{`Sélectionnez la taille d'étiquette`}</CardTitle>
</CardHeader>
@@ -29,14 +31,9 @@ export const StageSize = () => {
<Button onClick={() => handleStageSizeSelect(410, 289)}>
Grand Format
</Button>
</div>
</div>*/}
{/*<CardFooter className="mt-8 justify-between">*/}
{/* <Button variant="ghost" onClick={() => setOpen(false)}>*/}
{/* Cancel*/}
{/* </Button>*/}
{/* <Button onClick={handleSizeSelect}>Submit</Button>*/}
{/*</CardFooter>*/}
<ModelSelector types={types} models={models} />
</Card>
);
};

View File

@@ -22,9 +22,6 @@ export default function TextInput() {
};
return (
<Card className="p-3">
<CardHeader className="pt-2">
<CardTitle className="text-center">Text</CardTitle>
</CardHeader>
<Textarea
id="description"
placeholder="Entrez votre text ..."

View File

@@ -0,0 +1,20 @@
import * as React from "react";
export const useMutationObserver = (
ref: React.MutableRefObject<HTMLElement | null>,
callback: MutationCallback,
options = {
attributes: true,
characterData: true,
childList: true,
subtree: true,
},
) => {
React.useEffect(() => {
if (ref.current) {
const observer = new MutationObserver(callback);
observer.observe(ref.current, options);
return () => observer.disconnect();
}
}, [ref, callback, options]);
};

View File

@@ -4,6 +4,7 @@ import type { AnyAction, PayloadAction } from "@reduxjs/toolkit";
import { createSlice, current } from "@reduxjs/toolkit";
import { v1 } from "uuid";
import { type WritableDraft } from "immer/src/types/types-external";
import { CANVAS_PADDING_X, CANVAS_PADDING_Y } from "@/consts/canvas-params";
export enum StageItemType {
Text = "text",
@@ -61,6 +62,16 @@ const defaultImageConfig = {
scaleY: 1,
};
const defaultBackgroundImageConfig = {
x: 0,
y: 0,
opacity: 1,
offsetX: 0,
offsetY: 0,
scaleX: 1,
scaleY: 1,
};
const addNewItemToHistory =
(
callback: (
@@ -195,6 +206,36 @@ export const appSlice = createSlice({
});
},
),
setBackgroundImage: addNewItemToHistory(
(
state,
action: PayloadAction<{
imageUrl: string;
}>,
currentHistoryEntry,
) => {
const image = action.payload;
if (!image) return;
const imageId = v1();
const newImage = {
type: StageItemType.Image,
id: imageId,
isBlocked: false,
params: {
imageUrl: action.payload.imageUrl,
width: state.history[0]?.stage.width,
height: state.history[0]?.stage.height,
...defaultBackgroundImageConfig,
},
} as const;
const newHistoryEntry = {
...currentHistoryEntry,
items: [...currentHistoryEntry.items, newImage],
};
state.history.push(newHistoryEntry);
},
),
// -
selectItem: addNewItemToHistory(
(state, action: PayloadAction<string>, currentHistoryEntry) => {
@@ -332,4 +373,5 @@ export const {
setBlockedItem,
goBack,
goForward,
setBackgroundImage,
} = appSlice.actions;

16
vercel.json Normal file
View File

@@ -0,0 +1,16 @@
{
"version": 2,
"name": "labels-app",
"builds": [
{
"src": "src/pages/index.ts",
"use": "@vercel/node"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "src/pages/index.ts"
}
]
}