Skip to content

Commit

Permalink
feat: read and create beanlink urls
Browse files Browse the repository at this point in the history
  • Loading branch information
marcelblijleven committed Jun 20, 2023
1 parent 39cd94b commit 758a89f
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 15 deletions.
22 changes: 14 additions & 8 deletions src/app/beanconqueror/share/create/components/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,19 @@ import {VarietyInformationFieldset} from "@/app/beanconqueror/share/create/compo
import HorizontalGroup from "@/app/beanconqueror/share/create/components/horizontal-group";
import {defaultVarietyInformation, formSchema} from "@/app/beanconqueror/share/create/form-schema";
import {createUrlFromFormSchema} from "@/app/beanconqueror/share/create/util/proto-helpers";
import QRCodeCard from "@/components/qrcode-card";
import {getBeanLink} from "@/app/beanconqueror/share/create/util/beanlink-helpers";
import ShareCard from "@/app/beanconqueror/share/create/components/share-card";


const BeanInformationForm = () => {
const [showQR, setShowQR] = useState(false);
const [url, setUrl] = useState("");
const [beanLinkUrl, setBeanLinkUrl] = useState("");

const form = useForm<formSchema>({
resolver: zodResolver(formSchema),
defaultValues: {
name: "",
coffeeName: "",
roaster: "",
weight: "",
cost: "",
Expand All @@ -55,8 +57,12 @@ const BeanInformationForm = () => {
const blend = form.watch("beanMix");

const onSubmit = (values: formSchema) => {
setUrl(createUrlFromFormSchema(values));
const shareUrl = createUrlFromFormSchema(values);
setUrl(shareUrl);
setShowQR(true);
getBeanLink(shareUrl).then(r => setBeanLinkUrl(r)).catch(err => {
console.error(err);
});
}
useEffect(() => {
append({...defaultVarietyInformation} as any);
Expand All @@ -65,14 +71,14 @@ const BeanInformationForm = () => {
return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className={"w-full max-w-xl space-y-3"}>
<form onSubmit={form.handleSubmit(onSubmit)} className={"w-full space-y-3"}>
<Card>
<CardHeader>
<CardTitle>General information</CardTitle>
</CardHeader>
<CardContent className={"space-y-3"}>
<ControlledTextInput<formSchema>
name={"name"} label={"Name"}
name={"coffeeName"} label={"Name"}
placeholder={"Enter the name of the beans"}
control={form.control}
/>
Expand Down Expand Up @@ -113,7 +119,7 @@ const BeanInformationForm = () => {
enum={BeanMix}
/>

<fieldset>
<fieldset className={"space-y-3"}>
<Legend>More information</Legend>
<FormField<formSchema>
control={form.control}
Expand Down Expand Up @@ -204,11 +210,11 @@ const BeanInformationForm = () => {
</CardContent>
</Card>
<div className={"flex gap-2 justify-end"}>
<Button type={"submit"}>Submit</Button>
<Button type={"submit"}>Get share link</Button>
</div>
</form>
</Form>
{showQR && <QRCodeCard value={url}/>}
{showQR && <ShareCard bcUrl={url} beanLinkUrl={beanLinkUrl}/>}

</>
)
Expand Down
19 changes: 19 additions & 0 deletions src/app/beanconqueror/share/create/components/share-card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import QRCodeCard from "@/components/qrcode-card";
import {Card, CardContent, CardHeader, CardTitle} from "@/components/ui/card";
import CopyContainer from "@/components/copy-container";

const ShareCard = ({bcUrl, beanLinkUrl}: {bcUrl: string, beanLinkUrl: string}) => (
<Card className={"w-full"}>
<CardHeader>
<CardTitle>Share or import</CardTitle>
</CardHeader>
<CardContent>
<div className={"flex flex-col space-y-2"}>
<CopyContainer value={beanLinkUrl} />
<QRCodeCard value={bcUrl} />
</div>
</CardContent>
</Card>
);

export default ShareCard
2 changes: 1 addition & 1 deletion src/app/beanconqueror/share/create/form-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const varietyInformationShape = z.object({
export type varietyInformationType = z.infer<typeof varietyInformationShape>

export const formSchema = z.object({
name: z.string(),
coffeeName: z.string(),
roaster: z.optional(z.string()),
buyDate: z.optional(z.date()),
roastingDate: z.optional(z.date()),
Expand Down
5 changes: 4 additions & 1 deletion src/app/beanconqueror/share/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import Form from "@/app/beanconqueror/share/create/components/form";

export default function CreateShareLinkPage() {
return (
<div className={"flex flex-col items-center w-full max-w-5xl space-y-4"}>
<div className={"flex flex-col items-center w-full max-w-3xl space-y-4"}>
<h1 className={"text-4xl md:text-6xl font-bold text-center"}>
Create a <span className={"gradient-text"}>Beanconqueror</span> share url
</h1>
<Form />
</div>
)
Expand Down
25 changes: 25 additions & 0 deletions src/app/beanconqueror/share/create/util/beanlink-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
interface BeanLinkResponse {
link: string;
error?: string | undefined,
name: string;
roaster?: string | null;
}

export async function getBeanLink(link: string): Promise<string> {
const response = await fetch("https://beanl.ink/add", {
method: "POST",
body: JSON.stringify({"link": link})
});
const data: BeanLinkResponse = await response.json();

if (!!data.error) {
throw new Error(data.error);
}

return data.link
}

export async function followBeanLink(link: string): Promise<string> {
const response = await fetch(link);
return response.url;
}
2 changes: 1 addition & 1 deletion src/app/beanconqueror/share/create/util/proto-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import BeanRoastInformation = beanconqueror.BeanRoastInformation;
import ICupping = beanconqueror.ICupping;

const formSchemaToBeanProto = (values: formSchema) => BeanProto.create({
name: values.name,
name: values.coffeeName,
buyDate: values.buyDate?.toISOString() || "",
roastingDate: values.roastingDate?.toISOString() || "",
note: values.notes || "",
Expand Down
9 changes: 8 additions & 1 deletion src/app/beanconqueror/share/view/components/shared-bean.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import BeanProto = beanconqueror.BeanProto;
import IBeanInformation = beanconqueror.IBeanInformation;
import LabelledValue from "@/app/beanconqueror/share/view/components/labelled-value";
import QRCodeCard from "@/components/qrcode-card";
import {followBeanLink} from "@/app/beanconqueror/share/create/util/beanlink-helpers";

const GeneralTabsContent = ({decoded}: {decoded: BeanProto}) => (
<>
Expand Down Expand Up @@ -96,11 +97,17 @@ const VarietyTabsContent = ({decoded}: {decoded: BeanProto}) => (
</>
)

const SharedBean = ({url, validUrl}: { url: string | undefined, validUrl: boolean }) => {
const SharedBean = ({url, validUrl, isBeanLink}: { url: string | undefined, validUrl: boolean, isBeanLink: boolean }) => {
if (!url || !validUrl) {
return null;
}

if (isBeanLink) {
followBeanLink(url).then(response => {
url = response;
});
}

let err;
let decoded;

Expand Down
7 changes: 4 additions & 3 deletions src/app/beanconqueror/share/view/components/view-link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {Button} from "@/components/ui/button";
import SharedBean from "@/app/beanconqueror/share/view/components/shared-bean";

const LINK_RE = /^https:\/\/(?:www\.)?beanconqueror.com\/?\?.*$/;
const BEANLINK_RE = /^https:\/\/beanl.ink\/l\/.*$/;

const ViewLink = () => {
const [url, setUrl] = useState<string>("");
const [err, setErr] = useState<boolean>(false);

const onChange = (event: ChangeEvent<HTMLInputElement>) => {
const value = event.target.value;
setErr(value === "" || !value.match(LINK_RE));
const value = event.target.value.trim();
setErr(value === "" || (!value.match(LINK_RE) && !value.match(BEANLINK_RE)));
setUrl(value);
}

Expand All @@ -31,7 +32,7 @@ const ViewLink = () => {
/>
<Button onClick={() => setUrl("")}>Clear</Button>
</div>
<SharedBean url={url} validUrl={!err} />
<SharedBean url={url} validUrl={!err} isBeanLink={!!url.match(BEANLINK_RE)} />
</div>
)
}
Expand Down
30 changes: 30 additions & 0 deletions src/components/copy-container.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
"use client"

import {Button} from "@/components/ui/button";
import {Check, Copy, LucideIcon} from "lucide-react";
import {createElement, useState} from "react";

const CopyContainer = ({value}: {value: string}) => {
const [icon, setIcon] = useState<LucideIcon>(Copy);

const onClick = () => {
const writeToClipboard = async () => {
await navigator.clipboard.writeText(value);
setIcon(Check);
setTimeout(() => setIcon(Copy), 5000);
}

writeToClipboard();
}

return (
<div className={"flex items-center justify-between rounded-lg border bg-card text-card-foreground shadow-sm"}>
<div className={"text-sm p-2"}>{value}</div>
<Button variant={"ghost"} onClick={onClick}>
{createElement(icon)}
</Button>
</div>
)
}

export default CopyContainer

0 comments on commit 758a89f

Please sign in to comment.