BuzzPay.tsx raw
1 import { AlertTriangleIcon, CopyIcon, ExternalLinkIcon } from "lucide-react";
2 import React from "react";
3 import { toast } from "sonner";
4 import { AppDetailConnectedApps } from "src/components/connections/AppDetailConnectedApps";
5 import { AppStoreDetailHeader } from "src/components/connections/AppStoreDetailHeader";
6 import { appStoreApps } from "src/components/connections/SuggestedAppData";
7 import Loading from "src/components/Loading";
8 import QRCode from "src/components/QRCode";
9 import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert";
10 import { Button } from "src/components/ui/button";
11 import { LoadingButton } from "src/components/ui/custom/loading-button";
12 import { Input } from "src/components/ui/input";
13 import { Label } from "src/components/ui/label";
14 import { useApps } from "src/hooks/useApps";
15 import { copyToClipboard } from "src/lib/clipboard";
16 import { createApp } from "src/requests/createApp";
17 import { handleRequestError } from "src/utils/handleRequestError";
18 import { openLink } from "src/utils/openLink";
19
20 export function BuzzPay() {
21 const [name, setName] = React.useState("");
22 const [isLoading, setLoading] = React.useState(false);
23 const { data: appsData, mutate: reloadApps } = useApps(undefined, undefined, {
24 appStoreAppId: "buzzpay",
25 });
26 const [posUrl, setPosUrl] = React.useState("");
27
28 const appStoreApp = appStoreApps.find((app) => app.id === "buzzpay");
29 if (!appStoreApp) {
30 return null;
31 }
32 if (!appsData) {
33 return <Loading />;
34 }
35
36 const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
37 event.preventDefault();
38 setLoading(true);
39 (async () => {
40 try {
41 const createAppResponse = await createApp({
42 name,
43 scopes: ["get_info", "lookup_invoice", "make_invoice"],
44 isolated: true,
45 metadata: {
46 app_store_app_id: "buzzpay",
47 },
48 });
49
50 setPosUrl(
51 `https://pos.albylabs.com/#/?nwc=${btoa(createAppResponse.pairingUri)}&label=${encodeURIComponent(name)}`
52 );
53
54 await reloadApps();
55
56 toast("BuzzPay PoS connection created");
57 } catch (error) {
58 handleRequestError("Failed to create PoS connection", error);
59 }
60 setLoading(false);
61 })();
62 };
63
64 return (
65 <div className="grid gap-5">
66 <AppStoreDetailHeader appStoreApp={appStoreApp} contentRight={null} />
67 {posUrl && (
68 <div className="max-w-lg flex flex-col gap-5">
69 <p>
70 Open the PoS link below and share it with your employees and devices
71 you want to use the PoS with.
72 </p>
73 <Alert>
74 <AlertTriangleIcon />
75 <AlertTitle>
76 Save this link and add it to your home screen
77 </AlertTitle>
78 <AlertDescription>
79 This link will only be shown once and can't be retrieved
80 afterwards. Please make sure to keep it somewhere safe.
81 </AlertDescription>
82 </Alert>
83
84 <div className="flex flex-col items-center">
85 <QRCode value={posUrl} />
86 </div>
87 <div className="flex flex-col gap-3">
88 <Input disabled readOnly type="text" value={posUrl} />
89 <Button
90 className="w-full"
91 onClick={() => copyToClipboard(posUrl)}
92 variant="outline"
93 >
94 <CopyIcon />
95 Copy
96 </Button>
97 <Button
98 className="w-full"
99 onClick={() => openLink(posUrl)}
100 variant="outline"
101 >
102 <ExternalLinkIcon />
103 Open
104 </Button>
105 </div>
106 </div>
107 )}
108 {!posUrl && (
109 <>
110 <div className="max-w-lg flex flex-col gap-5">
111 <p className="text-muted-foreground">
112 BuzzPay works by creating read-only connections to your Alby Hub.
113 </p>
114 <ul className="text-muted-foreground">
115 <li>🔒 Allow employees to collect but never spend your funds</li>
116 <li>🔗 Sharable link that can be used on any device</li>
117 <li>âš¡ Lightning fast transactions directly to your Alby Hub</li>
118 </ul>
119 <form
120 onSubmit={handleSubmit}
121 className="flex flex-col items-start gap-5 max-w-lg"
122 >
123 <div className="w-full grid gap-1.5">
124 <Label htmlFor="name">PoS Name</Label>
125 <Input
126 autoFocus
127 type="text"
128 name="name"
129 value={name}
130 id="name"
131 onChange={(e) => setName(e.target.value)}
132 required
133 autoComplete="off"
134 />
135 </div>
136 <LoadingButton loading={isLoading} type="submit">
137 Next
138 </LoadingButton>
139 </form>
140 </div>
141 <AppDetailConnectedApps appStoreApp={appStoreApp} showTitle />
142 </>
143 )}
144 </div>
145 );
146 }
147