diff --git a/package.json b/package.json index 5a68629..d0a8ff7 100755 --- a/package.json +++ b/package.json @@ -10,17 +10,22 @@ "preview": "vite preview" }, "dependencies": { + "axios": "^1.8.4", + "cron": "^4.1.3", "dayjs": "^1.11.13", + "js-cookie": "^3.0.5", "preline": "^2.4.1", "react": "^18.3.1", "react-dom": "^18.3.1", "react-fast-marquee": "^1.6.5", "react-helmet": "^6.1.0", "react-icons": "^5.3.0", + "react-qr-code": "^2.0.15", "react-router-dom": "^6.26.2" }, "devDependencies": { "@eslint/js": "^9.9.0", + "@types/js-cookie": "^3.0.6", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", diff --git a/src/config/host.tsx b/src/config/host.tsx new file mode 100755 index 0000000..60adfb3 --- /dev/null +++ b/src/config/host.tsx @@ -0,0 +1,22 @@ +const MODE: "dev" | "staging" | "prod" = "dev"; + +const DEV_HOST = "http://next-api.ferinesia.local"; +const STAGING_HOST = ""; +const PROD_HOST = "https://prod-api-2.ferinesia.com"; + +const CURRENT_HOST: Record<"dev" | "staging" | "prod", string> = { + dev: DEV_HOST, + staging: STAGING_HOST, + prod: PROD_HOST +}; + +const endpoints = { + me: "/user/api/authentication/schedule_board/me", + qr_code_login: "/user/api/authentication/schedule_board/login_token", + login: "/user/api/authentication/schedule_board/login", + schedule_entries: "/schedules/api/schedule_board/get_schedule_entries" +} + +const activeHost = CURRENT_HOST[MODE]; + +export {activeHost, endpoints}; \ No newline at end of file diff --git a/src/functions/axios-instance.tsx b/src/functions/axios-instance.tsx new file mode 100755 index 0000000..11535bc --- /dev/null +++ b/src/functions/axios-instance.tsx @@ -0,0 +1,21 @@ +import { activeHost } from "@/config/host"; +import axios from "axios"; + +interface AxiosInstanceProp +{ + bearerToken?: string +} + + +const createAxiosInstance = (prop: AxiosInstanceProp) => { + const axiosInstance = axios.create({ + baseURL: activeHost, + headers: { + "Authorization": `Bearer ${prop.bearerToken}` + } + }); + + return axiosInstance; +} + +export {createAxiosInstance}; \ No newline at end of file diff --git a/src/index.css b/src/index.css index 11846d6..241a78e 100755 --- a/src/index.css +++ b/src/index.css @@ -4,4 +4,15 @@ html{ font-family: 'Inter'; -} \ No newline at end of file +} + +/* HTML:
*/ +.loader { + width: 50px; + aspect-ratio: 1; + border-radius: 50%; + border: 8px solid; + border-color: #000 #0000; + animation: l1 1s infinite; +} +@keyframes l1 {to{transform: rotate(.5turn)}} \ No newline at end of file diff --git a/src/main.tsx b/src/main.tsx index 91c1f3f..ceb49ed 100755 --- a/src/main.tsx +++ b/src/main.tsx @@ -5,6 +5,7 @@ import { createHashRouter, RouterProvider } from 'react-router-dom' import {Index as HomepageIndex} from '@/pages/homepage' import {Helmet} from "react-helmet" import Login from '@/pages/auth/login' +import QRCodeLogin from './pages/auth/qr-code-scan' const router = createHashRouter([ { @@ -14,7 +15,11 @@ const router = createHashRouter([ { path: "/login", element: - } + }, + { + path: "/qr-login", + element: + }, ]) createRoot(document.getElementById('root')!).render( diff --git a/src/pages/auth/login.tsx b/src/pages/auth/login.tsx index efcbaf7..2247b77 100755 --- a/src/pages/auth/login.tsx +++ b/src/pages/auth/login.tsx @@ -1,22 +1,77 @@ -import ferinesiaLogo from "@/assets/logos/ferinesia-logo-thin.png" -import { useEffect, useState } from "react" -import { useLocation } from "react-router-dom" +import ferinesiaLogo from "@/assets/logos/ferinesia-logo-thin.png"; +import { endpoints } from "@/config/host"; +import { createAxiosInstance } from "@/functions/axios-instance"; +import { useEffect, useState } from "react"; +import { useLocation, useSearchParams } from "react-router-dom"; +import Cookies from "js-cookie"; +import { AxiosError } from "axios"; /** * Copyright: Task Master (https://tailwindflex.com/@task_master) * URL: https://tailwindflex.com/@task_master/login-form-with-social-login-buttons */ -export interface InputValidationError{ - type: string, - message: string +export interface InputValidationError { + type: string; + message: string; } export default function Login() { - const [isLoading, setIsLoading] = useState(false) + const [searchParams] = useSearchParams(); - const [username, setUsername] = useState("") - const [password, setPassword] = useState("") + const [isLoading, setIsLoading] = useState(false); + const [loginToken, setLoginToken] = useState(null); + const [terminalID, setTerminalID] = useState(null); + + const [isSuccessLogin, setIsSuccessLogin] = useState(null); + + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + + const [inputValidationMsg, setInputValidationMsg] = useState([]); + + const doLogin = async () => { + let validationErrors = [] + if(username.length <= 0){ + validationErrors.push({type: "username", message: "Username tidak boleh kosong"}) + } + if(password.length <= 0){ + validationErrors.push({type: "password", message: "Password tidak boleh kosong"}) + } + setInputValidationMsg(validationErrors) + + if(validationErrors.length > 0) + return + + setIsLoading(true) + try{ + const {data} = await createAxiosInstance({}).post(endpoints.login, { + username, + password, + token: loginToken + }); + setIsSuccessLogin(true); + return + } + catch(err){ + if(err instanceof AxiosError){ + if(err.response?.data?.meta?.is_error){ + validationErrors.push({type: "username", message: err.response?.data?.meta?.message}) + } + } + else + validationErrors.push({type: "username", message: "Terjadi kesalahan saat memproses data"}) + } + finally{ + setInputValidationMsg(validationErrors) + setIsLoading(false) + } + } + + useEffect(() => { + setLoginToken(searchParams.get("state") ?? null); + setTerminalID(searchParams.get("terminal_id") ?? null); + }); return (
@@ -29,67 +84,102 @@ export default function Login() {
-
- - setUsername(e.currentTarget.value)} - /> - {/* { - inputValidationMsg.find(x => x.type == "username") && - {inputValidationMsg.find(x => x.type == "username")!.message} - } */} -
-
- - setPassword(e.currentTarget.value)} - /> - {/* { - inputValidationMsg.find(x => x.type == "password") && - {inputValidationMsg.find(x => x.type == "password")!.message} - } */} -
-
- -
+ {/* { + inputValidationMsg.find(x => x.type == "username") && + {inputValidationMsg.find(x => x.type == "username")!.message} + } */} +
+
+ + setUsername(e.currentTarget.value)} + /> + { + inputValidationMsg.find(x => x.type == "username") && + {inputValidationMsg.find(x => x.type == "username")!.message} + } +
+
+ + setPassword(e.currentTarget.value)} + /> + { + inputValidationMsg.find(x => x.type == "password") && + {inputValidationMsg.find(x => x.type == "password")!.message} + } +
+
+ {terminalID != null && loginToken != null && ( + + )} + + {(terminalID == null || loginToken == null) && Terminal ID dan Token Akses tidak valid.} +
+ + } + { + isSuccessLogin &&
+

Sukses

+
Berhasil melakukan login untuk penampil jadwal. Anda dapat menutup halaman ini.
+
+ } - ) + ); } diff --git a/src/pages/auth/qr-code-scan.tsx b/src/pages/auth/qr-code-scan.tsx new file mode 100755 index 0000000..1175d86 --- /dev/null +++ b/src/pages/auth/qr-code-scan.tsx @@ -0,0 +1,82 @@ +import ferinesiaLogo from "@/assets/logos/ferinesia-logo-thin.png"; +import { endpoints } from "@/config/host"; +import { createAxiosInstance } from "@/functions/axios-instance"; +import { useEffect, useState } from "react"; +import QRCode from "react-qr-code"; +import Cookies from "js-cookie"; + +export default function QRCodeLogin() { + const [isLoadingQR, setIsLoadingQR] = useState(true); + const [terminalID, setTerminalID] = useState(null); + const [qrCodeLogin, setQRCodeLogin] = useState(null); + + const getQRCodeLogin = async () => { + try{ + const {data} = await createAxiosInstance({}).get(endpoints.qr_code_login); + const loginToken = data.data.token; + setQRCodeLogin(`https://schedule-board.ferinesia.com/#/login?state=${loginToken}&terminal_id=${data.data.terminal_id}`); + setTerminalID(data.data.terminal_id); + setIsLoadingQR(false); + + setInterval(async () => { + try{ + const {data} = await createAxiosInstance({bearerToken: loginToken}).get(endpoints.me); + localStorage.setItem("HARBOR_NAME", data.data.harbor.name); + localStorage.setItem("HARBOR_ADDRESS", data.data.harbor.address); + Cookies.set("ferinesia_schedule_board_token", loginToken); + location.href = "/"; + } + catch(err){ + + } + }, 5000); + + setInterval(() => { + location.reload(); + }, 5 * 60 * 1000); + } + catch(err){ + alert("Tidak dapat mengambil QRCode, silahkan coba kembali."); + } + } + + useEffect(() => { + getQRCodeLogin(); + }, []); + + return ( + <> +
+
+
+
+ {isLoadingQR && ( +
+
+
+ )} + {!isLoadingQR && qrCodeLogin != null && ( + + )} + { + !isLoadingQR && Terminal ID: {terminalID ?? "N/A"} + } +
+
+
+ +

+ FERINESIA +

+
+

+ Silahkan melakukan login dengan mengunjungi URL yang tesedia + pada QRCode disebelah kiri. +

+
+
+
+
+ + ); +} diff --git a/src/pages/homepage/index.tsx b/src/pages/homepage/index.tsx index cc99b21..4bf9fb3 100755 --- a/src/pages/homepage/index.tsx +++ b/src/pages/homepage/index.tsx @@ -21,6 +21,11 @@ import { useEffect, useState } from "react"; import importData from "@/data/17_10_2024_TTE_BASTIONG.json"; import importCompanyInfo from "@/data/LogoLocation.json"; import { ScheduleStatus } from "@/constants/ScheduleStatus"; +import Cookies from "js-cookie"; +import { createAxiosInstance } from "@/functions/axios-instance"; +import { AxiosError } from "axios"; +import { endpoints } from "@/config/host"; +import { CronJob } from 'cron'; dayjs.extend(utc); dayjs.extend(timezone); @@ -35,11 +40,21 @@ interface Schedule { ship_name: string; } +interface AccountInfo { + harbor: { + name: string; + address: string; + code: string; + }; +} + export function Index() { const [timezone, setTimezone] = useState("N/A"); const [clock, setClock] = useState("00:00:00"); const [date, setDate] = useState(""); const [currentScheduleIndex, setCurrentScheduleIndex] = useState(0); + const [schedules, setSchedules] = useState([]); + const [marqueeTexts, setMarqueeTexts] = useState([]); const [grouppedSchedules, setGrouppedSchedules] = useState< { key: string; @@ -51,15 +66,7 @@ export function Index() { importCompanyInfo ); - const [data, setData] = useState<{ - harbor: { - name: string; - address: string; - }; - embed_link: string[]; - marquee_text: string[]; - schedules: Schedule[]; - }>(importData); + const [data, setData] = useState(null); const chunkArray = (array: Schedule[], chunkSize: number) => { const result = []; @@ -75,6 +82,7 @@ export function Index() { dayjs(x.departure_datetime).unix() - dayjs(y.departure_datetime).unix() ); const scheduleChunks = chunkArray(sortDates, chunkSize); + const tempResult = []; const todayDatetime = dayjs(); @@ -178,7 +186,7 @@ export function Index() { setDate(_date); }, 1000); - setGrouppedSchedules(groupDepartureDatetimes(data.schedules, 4)); + setGrouppedSchedules(groupDepartureDatetimes(schedules, 4)); }, []); useEffect(() => { @@ -191,6 +199,64 @@ export function Index() { } }, [grouppedSchedules]); + + const checkAuth = async () => { + const authToken = Cookies.get("ferinesia_schedule_board_token"); + try{ + const {data} = await createAxiosInstance({bearerToken: authToken}).get(endpoints.me); + setData({...data, harbor: {name: data.data.harbor.name, address: data.data.harbor.address, code: data.data.harbor.code}}); + setMarqueeTexts(data.data.marquee_texts); + } + catch(err){ + if(err instanceof AxiosError){ + if(err.response?.data?.meta?.code == "E_UNAUTHENTICATED"){ + location.href = "/#/qr-login" + } + } + } + } + + const fetchSchedules = async () => { + const authToken = Cookies.get("ferinesia_schedule_board_token"); + const firstDatetime = dayjs().format("YYYY-MM-DD HH:mm:ss"); + const secondDatetime = dayjs().add(7, 'day').format("YYYY-MM-DD HH:mm:ss"); + try{ + const {data} = await createAxiosInstance({bearerToken: authToken}).post(endpoints.schedule_entries, { + start_departure_date: firstDatetime, + end_departure_date: secondDatetime + }); + + let tempSchedules: Schedule[] = []; + for(const schedule of data.data.schedules){ + tempSchedules.push({ + departure_datetime: schedule.departure_datetime, + dock_name: "Dek 1", + destination: schedule.destination, + status: ScheduleStatus.AVAILABLE, + company_code: schedule.company_code, + ship_name: schedule.ship_name + }); + } + + setGrouppedSchedules(groupDepartureDatetimes(tempSchedules, 4)); + } + catch(err){ + + } + } + + useEffect(() => { + checkAuth(); + // equivalent job using the "from" static method, providing parameters as an object + CronJob.from({ + cronTime: '*/10 * * * *', + onTick: fetchSchedules, + start: true, + timeZone: 'Asia/Jakarta', + runOnInit: true + }); + }, []); + return ( <>
@@ -205,7 +271,7 @@ export function Index() {

- PELABUHAN BASTIONG + PELABUHAN {data?.harbor.name ?? "N/A"} [{data?.harbor.code ?? "N/A"}]

@@ -233,7 +299,7 @@ export function Index() { className="text-center text-4xl uppercase" style={{ width: "10%", letterSpacing: "3px" }} > - JAM + WAKTU BERANGKAT
- {data.marquee_text.map((x) => ( + {marqueeTexts.map((x) => (

{x}

))}
diff --git a/yarn.lock b/yarn.lock index 706669b..4b9cd71 100755 --- a/yarn.lock +++ b/yarn.lock @@ -562,6 +562,16 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== +"@types/js-cookie@^3.0.6": + version "3.0.6" + resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.6.tgz#a04ca19e877687bd449f5ad37d33b104b71fdf95" + integrity sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ== + +"@types/luxon@~3.4.0": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== + "@types/prop-types@*": version "15.7.12" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" @@ -746,6 +756,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + autoprefixer@^10.4.20: version "10.4.20" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b" @@ -758,6 +773,15 @@ autoprefixer@^10.4.20: picocolors "^1.0.1" postcss-value-parser "^4.2.0" +axios@^1.8.4: + version "1.8.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.4.tgz#78990bb4bc63d2cae072952d374835950a82f447" + integrity sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -800,6 +824,14 @@ browserslist@^4.23.1, browserslist@^4.23.3: node-releases "^2.0.18" update-browserslist-db "^1.1.0" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -871,6 +903,13 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" @@ -886,6 +925,14 @@ convert-source-map@^2.0.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== +cron@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/cron/-/cron-4.1.3.tgz#42661cebdf15483f90f63e799c9dbd33a298265c" + integrity sha512-HETm5kgivcdfboOmBIzq0cfC9c5bRilWZ1p7PWwnOMmbWviwIU6mPgZbeqbj5i0AzNan6P68WDTDEDezhKjOng== + dependencies: + "@types/luxon" "~3.4.0" + luxon "~3.6.0" + cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -922,6 +969,11 @@ deep-is@^0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -932,6 +984,15 @@ dlv@^1.1.3: resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== + dependencies: + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" + eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" @@ -952,6 +1013,33 @@ emoji-regex@^9.2.2: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + esbuild@^0.21.3: version "0.21.5" resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.21.5.tgz#9ca301b120922959b766360d8ac830da0d02997d" @@ -1165,6 +1253,11 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + foreground-child@^3.1.0: version "3.3.0" resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" @@ -1173,6 +1266,16 @@ foreground-child@^3.1.0: cross-spawn "^7.0.0" signal-exit "^4.0.1" +form-data@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" + integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + mime-types "^2.1.12" + fraction.js@^4.3.7: version "4.3.7" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" @@ -1193,6 +1296,30 @@ gensync@^1.0.0-beta.2: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1234,6 +1361,11 @@ globals@^15.9.0: resolved "https://registry.yarnpkg.com/globals/-/globals-15.9.0.tgz#e9de01771091ffbc37db5714dab484f9f69ff399" integrity sha512-SmSKyLLKFbSr6rptvP8izbyxJL4ILwqO9Jg23UA0sDlGlu58V59D1//I3vlc0KJphVdUR7vMjHIplYnzBxorQA== +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graphemer@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6" @@ -1249,6 +1381,18 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-symbols@^1.0.3, has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -1334,6 +1478,11 @@ jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.6.tgz#6c7f7398dd4b3142767f9a168af2f317a428d268" integrity sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w== +js-cookie@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc" + integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw== + "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" @@ -1432,6 +1581,16 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +luxon@~3.6.0: + version "3.6.1" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.6.1.tgz#d283ffc4c0076cb0db7885ec6da1c49ba97e47b0" + integrity sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ== + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1445,6 +1604,18 @@ micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.3" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -1659,7 +1830,7 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prop-types@^15.7.2: +prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -1668,11 +1839,21 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.13.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +qr.js@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f" + integrity sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ== + queue-microtask@^1.2.2: version "1.2.3" resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" @@ -1716,6 +1897,14 @@ 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-qr-code@^2.0.15: + version "2.0.15" + resolved "https://registry.yarnpkg.com/react-qr-code/-/react-qr-code-2.0.15.tgz#fbfc12952c504bcd64275647e9d1ea63251742ce" + integrity sha512-MkZcjEXqVKqXEIMVE0mbcGgDpkfSdd8zhuzXEl9QzYeNcw8Hq2oVIzDLWuZN2PQBwM5PWjc2S31K8Q1UbcFMfw== + dependencies: + prop-types "^15.8.1" + qr.js "0.0.0" + react-refresh@^0.14.2: version "0.14.2" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"