mirror of
https://github.com/fergalmoran/radio-otherway.git
synced 2026-01-06 09:04:01 +00:00
Add notification & scheduling callbacks
This commit is contained in:
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@@ -7,7 +7,12 @@
|
|||||||
"name": "Launch Program",
|
"name": "Launch Program",
|
||||||
"skipFiles": ["<node_internals>/**"],
|
"skipFiles": ["<node_internals>/**"],
|
||||||
"program": "${workspaceFolder}/server.js",
|
"program": "${workspaceFolder}/server.js",
|
||||||
"outFiles": ["${workspaceFolder}/**/*.js"]
|
"outFiles": ["${workspaceFolder}/**/*.js"],
|
||||||
|
"outputCapture": "std",
|
||||||
|
"resolveSourceMapLocations": [
|
||||||
|
"${workspaceFolder}/**",
|
||||||
|
"!**/node_modules/**"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
@@ -6,9 +6,11 @@
|
|||||||
"**/CVS": true,
|
"**/CVS": true,
|
||||||
"**/.DS_Store": true,
|
"**/.DS_Store": true,
|
||||||
"**/Thumbs.db": true,
|
"**/Thumbs.db": true,
|
||||||
".vscode":true,
|
".vscode": true,
|
||||||
".next": true,
|
".next": true,
|
||||||
".vercel": true
|
".vercel": true,
|
||||||
|
".working": true,
|
||||||
|
".yarn": true
|
||||||
},
|
},
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||||
|
|||||||
1068
.working/layout.html
1068
.working/layout.html
File diff suppressed because it is too large
Load Diff
5
.yarnrc
Normal file
5
.yarnrc
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
yarn-path ".yarn/releases/yarn-1.22.19.cjs"
|
||||||
12
package.json
12
package.json
@@ -20,30 +20,34 @@
|
|||||||
"@types/react": "18.0.28",
|
"@types/react": "18.0.28",
|
||||||
"@types/react-dom": "18.0.11",
|
"@types/react-dom": "18.0.11",
|
||||||
"@upstash/qstash": "^0.3.6",
|
"@upstash/qstash": "^0.3.6",
|
||||||
|
"axios": "^1.3.4",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
"daisyui": "^2.49.0",
|
"daisyui": "^2.49.0",
|
||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
"eslint": "8.32.0",
|
"eslint": "8.32.0",
|
||||||
"eslint-config-next": "13.1.5",
|
"eslint-config-next": "^13.2.2",
|
||||||
"feather-icons": "^4.29.0",
|
"feather-icons": "^4.29.0",
|
||||||
"firebase": "^9.17.1",
|
"firebase": "^9.17.1",
|
||||||
"firebase-admin": "^11.5.0",
|
"firebase-admin": "^11.5.0",
|
||||||
"firebase-functions": "^4.2.1",
|
"firebase-functions": "^4.2.1",
|
||||||
"fireschema": "^4.0.4",
|
"fireschema": "^4.0.4",
|
||||||
|
"http-status-codes": "^2.2.0",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"next": "^13.2.1",
|
"next": "^13.2.2",
|
||||||
"next-logger": "^3.0.1",
|
"next-logger": "^3.0.1",
|
||||||
"next-seo": "^5.15.0",
|
"next-seo": "^5.15.0",
|
||||||
"pino": "^8.11.0",
|
"pino": "^8.11.0",
|
||||||
"pino-logflare": "^0.3.12",
|
"pino-logflare": "^0.3.12",
|
||||||
"react": "18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-daisyui": "^3.0.3",
|
"react-daisyui": "^3.0.3",
|
||||||
"react-device-detect": "^2.2.3",
|
"react-device-detect": "^2.2.3",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-dropzone": "^14.2.3",
|
"react-dropzone": "^14.2.3",
|
||||||
"react-feather": "^2.0.10",
|
"react-feather": "^2.0.10",
|
||||||
|
"react-hook-form": "^7.43.2",
|
||||||
"react-hot-toast": "^2.4.0",
|
"react-hot-toast": "^2.4.0",
|
||||||
"react-icons": "^4.7.1",
|
"react-icons": "^4.7.1",
|
||||||
|
"react-phone-number-input": "^3.2.19",
|
||||||
"theme-change": "^2.3.0",
|
"theme-change": "^2.3.0",
|
||||||
"twilio": "^4.8.0",
|
"twilio": "^4.8.0",
|
||||||
"typescript": "4.9.4",
|
"typescript": "4.9.4",
|
||||||
|
|||||||
4
public/theme.js
Normal file
4
public/theme.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
(function initTheme() {
|
||||||
|
var theme = localStorage.getItem("theme") || "synthwave";
|
||||||
|
document.querySelector("html").setAttribute("data-theme", theme);
|
||||||
|
})();
|
||||||
13
src/app/(auth)/layout.tsx
Normal file
13
src/app/(auth)/layout.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const RootLayout = ({ children }: React.PropsWithChildren) => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-wrap w-full justify-evenly">
|
||||||
|
<div className="max-w-lg p-10 mt-6 rounded-md shadow-md font-body bg-base-100 text-base-content md:flex-1">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RootLayout;
|
||||||
@@ -2,12 +2,7 @@ import { LoginPage } from "@/components/pages/auth";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const Login = async () => {
|
const Login = async () => {
|
||||||
return (
|
return <LoginPage />;
|
||||||
<>
|
|
||||||
<div className="flex flex-wrap w-full justify-evenly">
|
|
||||||
<LoginPage />
|
|
||||||
</div>
|
|
||||||
</>);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Login;
|
export default Login;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import { SignupPage } from "@/components/pages/auth";
|
import { SignupPage } from "@/components/pages/auth";
|
||||||
|
|
||||||
const Signup = async () => {
|
const Signup = async () => {
|
||||||
return <div className="flex flex-wrap w-full justify-evenly"><SignupPage /></div>;
|
return <SignupPage />;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Signup;
|
export default Signup;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
import Script from "next/script";
|
||||||
|
|
||||||
export default function Head() {
|
export default function Head() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<title>Radio::Otherway</title>
|
<title>Radio::Otherway</title>
|
||||||
@@ -31,7 +31,6 @@ export default function Head() {
|
|||||||
<link rel="manifest" href="/site.webmanifest" />
|
<link rel="manifest" href="/site.webmanifest" />
|
||||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5" />
|
||||||
{/*need to include this here to avoid the FLUC on initial load*/}
|
{/*need to include this here to avoid the FLUC on initial load*/}
|
||||||
<script src="https://unpkg.com/theme-change@2.2.0/index.js" />
|
|
||||||
<meta name="msapplication-TileColor" content="#da532c" />
|
<meta name="msapplication-TileColor" content="#da532c" />
|
||||||
<meta name="theme-color" content="#ffffff" />
|
<meta name="theme-color" content="#ffffff" />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @next/next/no-before-interactive-script-outside-document */
|
||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
@@ -5,37 +6,39 @@ import { Raleway } from "@next/font/google";
|
|||||||
import { NavBar, PushNotificationWrapper } from "@/components/layout";
|
import { NavBar, PushNotificationWrapper } from "@/components/layout";
|
||||||
import { AuthUserProvider } from "@/lib/auth/authUserContext";
|
import { AuthUserProvider } from "@/lib/auth/authUserContext";
|
||||||
import { themeChange } from "theme-change";
|
import { themeChange } from "theme-change";
|
||||||
|
import Script from "next/script";
|
||||||
|
import logger from "@/lib/util/logging";
|
||||||
|
|
||||||
const font = Raleway({
|
const font = Raleway({
|
||||||
weight: ["400", "700"],
|
weight: ["400", "700"],
|
||||||
subsets: ["latin"],
|
subsets: ["latin"],
|
||||||
variable: "--font-raleway"
|
variable: "--font-raleway",
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function RootLayout({
|
const RootLayout = ({ children }: React.PropsWithChildren) => {
|
||||||
children
|
|
||||||
}: {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}) {
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
|
logger.info("Bootstrapping application");
|
||||||
themeChange(false);
|
themeChange(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head />
|
<head>
|
||||||
<body className={`${font.className}`}>
|
<Script src="/theme.js" strategy="beforeInteractive" />
|
||||||
<AuthUserProvider>
|
</head>
|
||||||
<PushNotificationWrapper>
|
<body className={`${font.className}`}>
|
||||||
<div className="flex flex-col min-h-screen bg-base-100">
|
<AuthUserProvider>
|
||||||
<NavBar />
|
<PushNotificationWrapper>
|
||||||
<div className="items-end grow place-items-center bg-base-200 text-primary-content">
|
<div className="flex flex-col min-h-screen bg-base-100">
|
||||||
<main className=" text-base-content">{children}</main>
|
<NavBar />
|
||||||
</div>
|
<div className="items-end grow place-items-center bg-base-200 text-primary-content">
|
||||||
</div>
|
<main className=" text-base-content">{children}</main>
|
||||||
</PushNotificationWrapper>
|
</div>
|
||||||
</AuthUserProvider>
|
</div>
|
||||||
</body>
|
</PushNotificationWrapper>
|
||||||
|
</AuthUserProvider>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
export default RootLayout;
|
||||||
|
|||||||
@@ -3,24 +3,37 @@ import React from "react";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import useFirebaseAuth from "@/lib/auth/useFirebaseAuth";
|
import useFirebaseAuth from "@/lib/auth/useFirebaseAuth";
|
||||||
import { IoLogoFacebook, IoLogoGoogle, IoLogoTwitter } from "react-icons/io";
|
import { IoLogoFacebook, IoLogoGoogle, IoLogoTwitter } from "react-icons/io";
|
||||||
|
import { AiOutlineExclamationCircle } from "react-icons/ai";
|
||||||
|
|
||||||
|
import { Info } from "react-feather";
|
||||||
|
|
||||||
const SignupPage = () => {
|
const SignupPage = () => {
|
||||||
const { signInWithGoogle, signInWithFacebook, signInWithTwitter, profile, signUp } =
|
const {
|
||||||
useFirebaseAuth();
|
signInWithGoogle,
|
||||||
|
signInWithFacebook,
|
||||||
|
signInWithTwitter,
|
||||||
|
profile,
|
||||||
|
signUp,
|
||||||
|
linkAccounts
|
||||||
|
} = useFirebaseAuth();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [error, setError] = React.useState("");
|
const [error, setError] = React.useState("");
|
||||||
const [email, setEmail] = React.useState("");
|
const [email, setEmail] = React.useState("");
|
||||||
const [password, setPassword] = React.useState("");
|
const [password, setPassword] = React.useState("");
|
||||||
const [confirmPassword, setConfirmPassword] = React.useState("");
|
const [confirmPassword, setConfirmPassword] = React.useState("");
|
||||||
|
|
||||||
|
const [linking, setLinking] = React.useState(false);
|
||||||
const register = async (
|
const register = async (
|
||||||
$event: React.SyntheticEvent<HTMLButtonElement>
|
$event: React.SyntheticEvent<HTMLButtonElement>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
|
setError("");
|
||||||
|
setLinking(false);
|
||||||
const result = await signUp(email, password);
|
const result = await signUp(email, password);
|
||||||
if (result === "auth/email-already-in-use") {
|
if (result === "auth/email-already-in-use") {
|
||||||
setError("This email address has already been used to create an account.");
|
setLinking(true);
|
||||||
|
setError("");
|
||||||
|
// setError("This email address has already been used to create an account.");
|
||||||
} else if (result === "auth/invalid-email") {
|
} else if (result === "auth/invalid-email") {
|
||||||
setError("Please enter a correct email address");
|
setError("Please enter a correct email address");
|
||||||
} else {
|
} else {
|
||||||
@@ -33,12 +46,20 @@ const SignupPage = () => {
|
|||||||
Create New Account
|
Create New Account
|
||||||
</h3>
|
</h3>
|
||||||
{error && (
|
{error && (
|
||||||
<div className="shadow-lg alert alert-error mb-4">
|
<div className="mb-4 shadow-lg alert alert-error">
|
||||||
<div>
|
<div>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="flex-shrink-0 w-6 h-6 stroke-current" fill="none"
|
<svg
|
||||||
viewBox="0 0 24 24">
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2"
|
className="flex-shrink-0 w-6 h-6 stroke-current"
|
||||||
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
<span>{error}</span>
|
<span>{error}</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -147,7 +168,6 @@ const SignupPage = () => {
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,26 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import React from "react";
|
import React, { useEffect, useMemo } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { User, Bell } from "react-feather";
|
import { User, Bell } from "react-feather";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useAuthUserContext } from "@/lib/auth/authUserContext";
|
import { useAuthUserContext } from "@/lib/auth/authUserContext";
|
||||||
import ProfilePageComponentProfile from "./ProfilePageComponentProfile";
|
import ProfilePageComponentProfile from "./ProfilePageComponentProfile";
|
||||||
import ProfilePageComponentNotifications from "./ProfilePageComponentNotifications";
|
import ProfilePageComponentNotifications from "./ProfilePageComponentNotifications";
|
||||||
|
import { SubmitHandler, useForm } from "react-hook-form";
|
||||||
|
import { users } from "@/lib/db";
|
||||||
|
import { doc, setDoc } from "firebase/firestore";
|
||||||
|
import ToastService from "@/components/widgets/toast";
|
||||||
|
import logger from "@/lib/util/logging";
|
||||||
|
import { removeUndefinedProperties } from "@/lib/util/objectUtils";
|
||||||
|
|
||||||
|
export type ProfileForm = {
|
||||||
|
displayName: string;
|
||||||
|
email: string;
|
||||||
|
about: string;
|
||||||
|
photoURL: string;
|
||||||
|
headerPhotoURL: string;
|
||||||
|
mobileNumber: string;
|
||||||
|
};
|
||||||
|
|
||||||
const ProfilePageComponent = () => {
|
const ProfilePageComponent = () => {
|
||||||
const { profile, loading } = useAuthUserContext();
|
const { profile, loading } = useAuthUserContext();
|
||||||
@@ -15,7 +30,6 @@ const ProfilePageComponent = () => {
|
|||||||
router.push("/");
|
router.push("/");
|
||||||
}
|
}
|
||||||
}, [profile, loading, router]);
|
}, [profile, loading, router]);
|
||||||
const [sendReminders, setSendReminders] = React.useState(false);
|
|
||||||
const subNavigation = [
|
const subNavigation = [
|
||||||
{ name: "profile", title: "Profile", icon: User },
|
{ name: "profile", title: "Profile", icon: User },
|
||||||
{
|
{
|
||||||
@@ -25,12 +39,59 @@ const ProfilePageComponent = () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
const [selectedItem, setSelectedItem] = React.useState("profile");
|
const [selectedItem, setSelectedItem] = React.useState("profile");
|
||||||
const [userName, setUserName] = React.useState("");
|
const {
|
||||||
const [password, setPassword] = React.useState("");
|
register,
|
||||||
const [about, setAbout] = React.useState("");
|
handleSubmit,
|
||||||
const [displayName, setDisplayName] = React.useState("");
|
setValue,
|
||||||
const [url, setUrl] = React.useState("");
|
reset,
|
||||||
const [image, setImage] = React.useState("");
|
watch,
|
||||||
|
control,
|
||||||
|
formState: { errors },
|
||||||
|
} = useForm<ProfileForm>({
|
||||||
|
defaultValues: useMemo(() => {
|
||||||
|
return profile;
|
||||||
|
}, [profile]),
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (profile) {
|
||||||
|
reset(profile);
|
||||||
|
}
|
||||||
|
// setValue([
|
||||||
|
// { displayName: profile?.displayName },
|
||||||
|
// { email: profile?.email },
|
||||||
|
// { about: profile?.about },
|
||||||
|
// { photoURL: profile?.photoURL },
|
||||||
|
// { headerPhotoURL: profile?.headerPhotoURL },
|
||||||
|
// { mobileNumber: profile?.mobileNumber },
|
||||||
|
// ]);
|
||||||
|
}, [profile, reset]);
|
||||||
|
const onSubmit: SubmitHandler<ProfileForm> = async (data) => {
|
||||||
|
console.log(data);
|
||||||
|
try {
|
||||||
|
const newProfile = removeUndefinedProperties({
|
||||||
|
displayName: data.displayName,
|
||||||
|
email: data.email,
|
||||||
|
about: data.about,
|
||||||
|
photoURL: data.photoURL,
|
||||||
|
headerPhotoURL: data.headerPhotoURL,
|
||||||
|
mobileNumber: data.mobileNumber,
|
||||||
|
lastSeen: new Date(),
|
||||||
|
});
|
||||||
|
const result = await setDoc(
|
||||||
|
doc(users, profile?.id),
|
||||||
|
Object.assign({}, newProfile),
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
console.log("ProfilePageComponentProfile", "_submitProfileForm", result);
|
||||||
|
ToastService.success("Successfully updated your profile", "Success");
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("ProfilePageComponentProfile", "_submitProfileForm", err);
|
||||||
|
ToastService.error("Failed to update your profile.");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
React.useEffect(() => {}, [selectedItem]);
|
React.useEffect(() => {}, [selectedItem]);
|
||||||
const _getView = () => {
|
const _getView = () => {
|
||||||
@@ -76,11 +137,30 @@ const ProfilePageComponent = () => {
|
|||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div className="p-4 divide-y lg:col-span-9">
|
<div className="p-4 divide-y lg:col-span-9">
|
||||||
{selectedItem === "profile" ? (
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<ProfilePageComponentProfile />
|
{selectedItem === "profile" ? (
|
||||||
) : (
|
<ProfilePageComponentProfile
|
||||||
<ProfilePageComponentNotifications />
|
register={register}
|
||||||
)}
|
profile={profile}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ProfilePageComponentNotifications
|
||||||
|
register={register}
|
||||||
|
control={control}
|
||||||
|
profile={profile}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div className="pt-5">
|
||||||
|
<div className="flex justify-end space-x-2">
|
||||||
|
<button type="button" className="btn-warning btn">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="submit" className="btn-success btn">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,54 +4,66 @@ import {
|
|||||||
PhoneNumber,
|
PhoneNumber,
|
||||||
RequestPushNotifications,
|
RequestPushNotifications,
|
||||||
} from "@/components/widgets/notifications";
|
} from "@/components/widgets/notifications";
|
||||||
|
import { UseFormRegister } from "react-hook-form";
|
||||||
|
import { ProfileForm } from "@/components/pages/profile/ProfilePageComponent";
|
||||||
|
import { Profile } from "@/models";
|
||||||
|
|
||||||
|
interface IProfilePageComponentNotificationsProps {
|
||||||
|
register: UseFormRegister<ProfileForm>;
|
||||||
|
control: any;
|
||||||
|
profile: Profile;
|
||||||
|
}
|
||||||
|
const ProfilePageComponentNotifications = ({
|
||||||
|
register,
|
||||||
|
control,
|
||||||
|
profile,
|
||||||
|
}: IProfilePageComponentNotificationsProps) => {
|
||||||
|
const [notificationPermissionsGranted, setNotificationPermissionsGranted] =
|
||||||
|
React.useState(Notification.permission !== "granted");
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setNotificationPermissionsGranted(Notification.permission !== "granted");
|
||||||
|
}, []);
|
||||||
|
|
||||||
const ProfilePageComponentNotifications = () => {
|
|
||||||
return (
|
return (
|
||||||
<form className="space-y-8 divide-y ">
|
<div className="space-y-8 divide-y sm:space-y-5">
|
||||||
<div className="space-y-8 divide-y sm:space-y-5">
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<h3 className="text-lg font-medium leading-6 ">Notifications</h3>
|
||||||
<h3 className="text-lg font-medium leading-6 ">Notifications</h3>
|
<p className="max-w-2xl mt-1 text-sm ">
|
||||||
<p className="max-w-2xl mt-1 text-sm ">
|
Here you can setup various different forms of notifications
|
||||||
Here you can setup various different forms of notifications
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
<div className="mt-6 space-y-6 sm:mt-5 sm:space-y-5">
|
||||||
<div className="mt-6 space-y-6 sm:mt-5 sm:space-y-5">
|
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
||||||
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
<HeadingSubComponent
|
||||||
<HeadingSubComponent
|
title="Browser Notifications"
|
||||||
title="Browser Notifications"
|
subHeading="We'll send you an alert through (this) browser when a show is about to start."
|
||||||
subHeading="We'll send you an alert through (this) browser when a show is about to start."
|
/>
|
||||||
/>
|
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
||||||
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
{notificationPermissionsGranted ? (
|
||||||
|
<div>Permissions granted</div>
|
||||||
|
) : (
|
||||||
<RequestPushNotifications />
|
<RequestPushNotifications />
|
||||||
</div>
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
</div>
|
||||||
<HeadingSubComponent
|
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
||||||
title="Phone number"
|
<HeadingSubComponent
|
||||||
subHeading="We'll try to send you a WhatsApp message when a show is about to start"
|
title="Phone number"
|
||||||
/>
|
subHeading="We'll try to send you a WhatsApp message when a show is about to start"
|
||||||
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
/>
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
||||||
<PhoneNumber
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
phoneNumber="12354"
|
<PhoneNumber name="mobileNumber" control={control} />
|
||||||
setPhoneNumber={(value) =>
|
|
||||||
console.log(
|
|
||||||
"ProfilePageComponentNotifications",
|
|
||||||
"setPhoneNumber",
|
|
||||||
value
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,205 +1,153 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { InputText } from "@/components/widgets/inputs";
|
|
||||||
import { HeadingSubComponent } from "@/components/widgets/text";
|
import { HeadingSubComponent } from "@/components/widgets/text";
|
||||||
import { useAuthUserContext } from "@/lib/auth/authUserContext";
|
|
||||||
import db, { users } from "@/lib/db";
|
|
||||||
import { debug } from "console";
|
|
||||||
import { doc, setDoc } from "firebase/firestore";
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import ToastService from "@/components/widgets/toast";
|
import { UseFormRegister } from "react-hook-form";
|
||||||
import logger from "@/lib/util/logging";
|
import { ProfileForm } from "@/components/pages/profile/ProfilePageComponent";
|
||||||
|
import { Profile } from "@/models";
|
||||||
|
import InputText from "@/components/widgets/inputs/InputText";
|
||||||
|
import { InputTextArea } from "@/components/widgets/inputs";
|
||||||
|
|
||||||
const ProfilePageComponentProfile = () => {
|
interface IProfilePageComponentProfileProps {
|
||||||
const { loading, profile } = useAuthUserContext();
|
register: UseFormRegister<ProfileForm>;
|
||||||
const [sendReminders, setSendReminders] = React.useState(false);
|
profile: Profile;
|
||||||
const [email, setEmail] = React.useState("");
|
}
|
||||||
const [password, setPassword] = React.useState("");
|
const ProfilePageComponentProfile = ({
|
||||||
const [about, setAbout] = React.useState("");
|
register,
|
||||||
const [displayName, setDisplayName] = React.useState("");
|
profile,
|
||||||
const [url, setUrl] = React.useState("");
|
}: IProfilePageComponentProfileProps) => {
|
||||||
const [image, setImage] = React.useState("");
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (profile) {
|
|
||||||
setEmail(profile.email as string);
|
|
||||||
setDisplayName(profile.displayName as string);
|
|
||||||
setAbout(profile.about as string);
|
|
||||||
}
|
|
||||||
}, [profile]);
|
|
||||||
const _submitProfileForm = async ($event: React.SyntheticEvent) => {
|
|
||||||
$event.preventDefault();
|
|
||||||
try {
|
|
||||||
const result = await setDoc(
|
|
||||||
doc(users, profile?.id),
|
|
||||||
Object.assign(
|
|
||||||
{},
|
|
||||||
{
|
|
||||||
email,
|
|
||||||
displayName,
|
|
||||||
about: about || "",
|
|
||||||
lastSeen: new Date()
|
|
||||||
}
|
|
||||||
),
|
|
||||||
{ merge: true }
|
|
||||||
);
|
|
||||||
console.log("ProfilePageComponentProfile", "_submitProfileForm", result);
|
|
||||||
ToastService.success("Successfully updated your profile", "Success");
|
|
||||||
} catch (err) {
|
|
||||||
logger.error("ProfilePageComponentProfile", "_submitProfileForm", err);
|
|
||||||
ToastService.error("Failed to update your profile.");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<form className="space-y-8 divide-y" onSubmit={_submitProfileForm}>
|
<div className="space-y-8 divide-y sm:space-y-5">
|
||||||
<div className="space-y-8 divide-y sm:space-y-5">
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<h3 className="text-lg font-medium leading-6 ">Profile</h3>
|
||||||
<h3 className="text-lg font-medium leading-6 ">Profile</h3>
|
<p className="max-w-2xl mt-1 text-sm ">
|
||||||
<p className="max-w-2xl mt-1 text-sm ">
|
This information will be displayed publicly so be careful what you
|
||||||
This information will be displayed publicly so be careful what you
|
share.
|
||||||
share.
|
</p>
|
||||||
</p>
|
</div>
|
||||||
</div>
|
<div className="mt-6 space-y-6 sm:mt-5 sm:space-y-5">
|
||||||
<div className="mt-6 space-y-6 sm:mt-5 sm:space-y-5">
|
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
||||||
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
<HeadingSubComponent
|
||||||
<HeadingSubComponent
|
title="Display Name"
|
||||||
title="Display Name"
|
subHeading="The name you would like others to see you as."
|
||||||
subHeading="The name you would like others to see you as."
|
/>
|
||||||
/>
|
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
||||||
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
|
||||||
<InputText
|
|
||||||
id="displayname"
|
|
||||||
type="text"
|
|
||||||
labelTitle="Display name"
|
|
||||||
value={displayName}
|
|
||||||
updateFormValue={(v) => {
|
|
||||||
setDisplayName(v);
|
|
||||||
}}
|
|
||||||
showLabel={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
|
||||||
<HeadingSubComponent
|
|
||||||
title="Email address"
|
|
||||||
subHeading="In case we need to get in touch with you"
|
|
||||||
/>
|
|
||||||
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
|
||||||
<div className="flex max-w-lg rounded-md shadow-sm">
|
|
||||||
<InputText
|
|
||||||
id="email"
|
|
||||||
type="email"
|
|
||||||
labelTitle="Email address"
|
|
||||||
value={email}
|
|
||||||
updateFormValue={(v) => setEmail(v)}
|
|
||||||
showLabel={false}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
|
||||||
<HeadingSubComponent
|
|
||||||
title="About"
|
|
||||||
subHeading="Tell us a little bit about yourself.. but not too much"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
|
||||||
<InputText
|
<InputText
|
||||||
id="about"
|
id="displayName"
|
||||||
type="textarea"
|
type="text"
|
||||||
labelTitle="About"
|
label="Display name"
|
||||||
value={about}
|
{...register("displayName")}
|
||||||
updateFormValue={(v) => setAbout(v)}
|
|
||||||
showLabel={false}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:items-center sm:gap-4 sm:border-t sm:pt-5">
|
<div className="space-x-3 sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
||||||
<HeadingSubComponent
|
<HeadingSubComponent
|
||||||
title="Photo"
|
title="Email address"
|
||||||
subHeading="Upload a picture to distinguish you from the rest"
|
subHeading="In case we need to get in touch with you"
|
||||||
/>
|
/>
|
||||||
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
||||||
<div className="flex items-center">
|
<div className="flex max-w-lg rounded-md shadow-sm">
|
||||||
<span className="w-12 h-12 overflow-hidden rounded-full">
|
<InputText
|
||||||
<svg
|
id="email"
|
||||||
className="w-full h-full "
|
type="email"
|
||||||
fill="currentColor"
|
label="Email address"
|
||||||
viewBox="0 0 24 24"
|
showLabel={false}
|
||||||
>
|
{...register("email")}
|
||||||
<path
|
/>
|
||||||
d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
||||||
</svg>
|
|
||||||
</span>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="px-3 py-2 ml-5 text-sm font-medium leading-4 border rounded-md shadow-sm hover: focus:outline-none focus:ring-2 focus:ring-offset-2"
|
|
||||||
>
|
|
||||||
Change
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
<div className="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
||||||
<HeadingSubComponent
|
<HeadingSubComponent
|
||||||
title="Cover photo"
|
title="About"
|
||||||
subHeading="Upload a wide photo for the top of your profile page"
|
subHeading="Tell us a little bit about yourself.. but not too much"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
||||||
|
<InputTextArea
|
||||||
|
id="about"
|
||||||
|
type="textarea"
|
||||||
|
label="About"
|
||||||
|
showLabel={false}
|
||||||
|
{...register("about")}
|
||||||
/>
|
/>
|
||||||
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
</div>
|
||||||
<div className="flex justify-center max-w-lg px-6 pt-5 pb-6 border-2 border-dashed rounded-md">
|
</div>
|
||||||
<div className="space-y-1 text-center">
|
|
||||||
<svg
|
<div className="sm:grid sm:grid-cols-3 sm:items-center sm:gap-4 sm:border-t sm:pt-5">
|
||||||
className="w-12 h-12 mx-auto "
|
<HeadingSubComponent
|
||||||
stroke="currentColor"
|
title="Photo"
|
||||||
fill="none"
|
subHeading="Upload a picture to distinguish you from the rest"
|
||||||
viewBox="0 0 48 48"
|
/>
|
||||||
aria-hidden="true"
|
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<span className="w-12 h-12 overflow-hidden rounded-full">
|
||||||
|
<svg
|
||||||
|
className="w-full h-full "
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="px-3 py-2 ml-5 text-sm font-medium leading-4 border rounded-md shadow-sm hover: focus:outline-none focus:ring-2 focus:ring-offset-2"
|
||||||
|
>
|
||||||
|
Change
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="sm:grid sm:grid-cols-3 sm:items-start sm:gap-4 sm:border-t sm:pt-5">
|
||||||
|
<HeadingSubComponent
|
||||||
|
title="Cover photo"
|
||||||
|
subHeading="Upload a wide photo for the top of your profile page"
|
||||||
|
/>
|
||||||
|
<div className="mt-1 sm:col-span-2 sm:mt-0">
|
||||||
|
<div className="flex justify-center max-w-lg px-6 pt-5 pb-6 border-2 border-dashed rounded-md">
|
||||||
|
<div className="space-y-1 text-center">
|
||||||
|
<svg
|
||||||
|
className="w-12 h-12 mx-auto "
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"
|
||||||
|
strokeWidth={2}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<div className="flex text-sm ">
|
||||||
|
<label
|
||||||
|
htmlFor="file-upload"
|
||||||
|
className="relative font-medium rounded-md cursor-pointer te focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2"
|
||||||
>
|
>
|
||||||
<path
|
<span>Upload a file</span>
|
||||||
d="M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02"
|
<input
|
||||||
strokeWidth={2}
|
id="file-upload"
|
||||||
strokeLinecap="round"
|
name="file-upload"
|
||||||
strokeLinejoin="round"
|
type="file"
|
||||||
|
className="sr-only"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</label>
|
||||||
<div className="flex text-sm ">
|
<p className="pl-1">or drag and drop</p>
|
||||||
<label
|
|
||||||
htmlFor="file-upload"
|
|
||||||
className="relative font-medium rounded-md cursor-pointer te focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2"
|
|
||||||
>
|
|
||||||
<span>Upload a file</span>
|
|
||||||
<input
|
|
||||||
id="file-upload"
|
|
||||||
name="file-upload"
|
|
||||||
type="file"
|
|
||||||
className="sr-only"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<p className="pl-1">or drag and drop</p>
|
|
||||||
</div>
|
|
||||||
<p className="text-xs ">PNG, JPG, GIF up to 10MB</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
<p className="text-xs ">PNG, JPG, GIF up to 10MB</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="pt-5">
|
</div>
|
||||||
<div className="flex justify-end space-x-2">
|
|
||||||
<button type="button" className="btn-warning btn">
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button type="submit" className="btn-success btn">
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,30 @@
|
|||||||
import React from "react";
|
import React, { forwardRef } from "react";
|
||||||
|
import ITextInputProps from "./props";
|
||||||
|
|
||||||
interface IInputTextProps {
|
const InputText = forwardRef<HTMLInputElement, ITextInputProps>(
|
||||||
id: string;
|
({ id, type, placeholder, label, showLabel = true, onChange, onBlur }, ref) => {
|
||||||
labelTitle: string;
|
|
||||||
labelStyle?: string;
|
|
||||||
type?: string;
|
|
||||||
containerStyle?: string;
|
|
||||||
value: string;
|
|
||||||
placeholder?: string;
|
|
||||||
showLabel?: boolean;
|
|
||||||
updateFormValue: (value: string) => void;
|
|
||||||
}
|
|
||||||
const InputText = ({
|
|
||||||
id,
|
|
||||||
labelTitle,
|
|
||||||
labelStyle,
|
|
||||||
type,
|
|
||||||
containerStyle,
|
|
||||||
value,
|
|
||||||
placeholder,
|
|
||||||
updateFormValue,
|
|
||||||
showLabel = true,
|
|
||||||
}: IInputTextProps) => {
|
|
||||||
|
|
||||||
const updateInputValue = (val: string) => {
|
return (
|
||||||
updateFormValue(val);
|
<React.Fragment>
|
||||||
};
|
{showLabel && (
|
||||||
const _getId = () => (type === "text" ? `inp_${id}` : `ta_${id}`);
|
<label className="label" htmlFor={id}>
|
||||||
const _getInput = () =>
|
<span className="label-text">{label}</span>
|
||||||
type === "textarea" ? (
|
</label>
|
||||||
<textarea
|
)}
|
||||||
id={_getId()}
|
<input
|
||||||
name={_getId()}
|
id={id}
|
||||||
rows={3}
|
className="w-full input-bordered input"
|
||||||
className="w-full h-24 textarea-bordered textarea"
|
type={type || "text"}
|
||||||
placeholder={placeholder || ""}
|
placeholder={placeholder || ""}
|
||||||
defaultValue={value}
|
onChange={onChange}
|
||||||
onChange={(e) => updateInputValue(e.target.value)}
|
onBlur={onBlur}
|
||||||
/>
|
ref={ref}
|
||||||
) : (
|
/>
|
||||||
<input
|
</React.Fragment>
|
||||||
id={_getId()}
|
|
||||||
className="w-full input-bordered input"
|
|
||||||
type={type || "text"}
|
|
||||||
placeholder={placeholder || ""}
|
|
||||||
value={value}
|
|
||||||
onChange={(e) => updateInputValue(e.target.value)}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
return (
|
}
|
||||||
<React.Fragment>
|
);
|
||||||
{showLabel && (
|
InputText.displayName = "InputTextAreaComponent";
|
||||||
<label className="label" htmlFor={_getId()}>
|
|
||||||
<span className="label-text">{labelTitle}</span>
|
|
||||||
</label>
|
|
||||||
)}
|
|
||||||
{_getInput()}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default InputText;
|
export default InputText;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
31
src/components/widgets/inputs/InputTextArea.tsx
Normal file
31
src/components/widgets/inputs/InputTextArea.tsx
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import React, { forwardRef, Ref } from "react";
|
||||||
|
import ITextInputProps from "./props";
|
||||||
|
|
||||||
|
|
||||||
|
const InputTextArea = forwardRef<HTMLTextAreaElement, ITextInputProps>(
|
||||||
|
({ id, placeholder, showLabel = true, label, onChange, onBlur }, ref) => {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{showLabel && (
|
||||||
|
<label className="label" htmlFor={id}>
|
||||||
|
<span className="label-text">{label}</span>
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
<textarea
|
||||||
|
id={id}
|
||||||
|
name={id}
|
||||||
|
rows={3}
|
||||||
|
className="w-full h-24 textarea-bordered textarea"
|
||||||
|
placeholder={placeholder || ""}
|
||||||
|
onChange={onChange}
|
||||||
|
onBlur={onBlur}
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
InputTextArea.displayName = "InputTextComponent";
|
||||||
|
export default InputTextArea;
|
||||||
|
|
||||||
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import ImageUpload from "./ImageUpload";
|
import ImageUpload from "./ImageUpload";
|
||||||
import InputText from "./InputText";
|
import InputText from "./InputText";
|
||||||
|
import InputTextArea from "./InputTextArea";
|
||||||
|
|
||||||
export { ImageUpload };
|
export { ImageUpload };
|
||||||
export { InputText };
|
export { InputTextArea };
|
||||||
|
|||||||
13
src/components/widgets/inputs/props.ts
Normal file
13
src/components/widgets/inputs/props.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { ChangeEventHandler } from "react";
|
||||||
|
|
||||||
|
export default interface ITextInputProps {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
placeholder?: string;
|
||||||
|
showLabel?: boolean;
|
||||||
|
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
onChange: ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
|
||||||
|
onBlur: ChangeEventHandler<HTMLInputElement | HTMLTextAreaElement>;
|
||||||
|
}
|
||||||
@@ -1,22 +1,25 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { InputText } from "../inputs";
|
import "react-phone-number-input/style.css";
|
||||||
|
import { Control, UseFormRegister } from "react-hook-form";
|
||||||
|
import { ProfileForm } from "@/components/pages/profile/ProfilePageComponent";
|
||||||
|
import { DefaultFormValues } from "react-phone-number-input/react-hook-form";
|
||||||
|
import PhoneInputWithCountry from "react-phone-number-input/react-hook-form";
|
||||||
|
|
||||||
interface PhoneNumberNotificationControlProps {
|
interface IPhoneNumberNotificationControlProps {
|
||||||
phoneNumber: string;
|
name: string;
|
||||||
setPhoneNumber: (number: string) => void;
|
control: any;
|
||||||
}
|
}
|
||||||
const PhoneNumberNotificationControl = ({
|
const PhoneNumberNotificationControl = ({
|
||||||
phoneNumber,
|
name,
|
||||||
setPhoneNumber,
|
control,
|
||||||
}: PhoneNumberNotificationControlProps) => {
|
}: IPhoneNumberNotificationControlProps) => {
|
||||||
return (
|
return (
|
||||||
<InputText
|
<PhoneInputWithCountry
|
||||||
showLabel={false}
|
name={name}
|
||||||
type="tel"
|
className="w-full input-bordered input"
|
||||||
value="phoneNumber"
|
defaultCountry="IE"
|
||||||
labelTitle="Phone number"
|
placeholder="Enter phone number"
|
||||||
updateFormValue={(value) => setPhoneNumber(value)}
|
control={control}
|
||||||
id="phone"
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import { InputText } from "../inputs";
|
import InputText from "../inputs/InputText";
|
||||||
|
import { UseFormRegister } from "react-hook-form";
|
||||||
|
import { ProfileForm } from "@/components/pages/profile/ProfilePageComponent";
|
||||||
|
import { Profile } from "@/models";
|
||||||
|
|
||||||
interface ISignalNotificationProps {
|
interface ISignalNotificationProps {
|
||||||
phoneNumber: string;
|
register: UseFormRegister<ProfileForm>;
|
||||||
setPhoneNumber: (number: string) => void;
|
profile: Profile;
|
||||||
}
|
}
|
||||||
const SignalNotification = ({
|
const SignalNotification = ({
|
||||||
phoneNumber,
|
register,
|
||||||
setPhoneNumber,
|
profile,
|
||||||
}: ISignalNotificationProps) => {
|
}: ISignalNotificationProps) => {
|
||||||
return (
|
return (
|
||||||
<InputText
|
<div>
|
||||||
showLabel={false}
|
{/* <InputText
|
||||||
type="tel"
|
id="signalNotification"
|
||||||
value="phoneNumber"
|
showLabel={false}
|
||||||
labelTitle="Phone number"
|
type="tel"
|
||||||
updateFormValue={(value) => setPhoneNumber(value)}
|
{...register('signalNotification')}
|
||||||
id="phone"
|
/> */}
|
||||||
/>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export default SignalNotification;
|
export default SignalNotification;
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import {
|
import {
|
||||||
createUserWithEmailAndPassword,
|
createUserWithEmailAndPassword,
|
||||||
|
EmailAuthCredential,
|
||||||
|
EmailAuthProvider,
|
||||||
FacebookAuthProvider,
|
FacebookAuthProvider,
|
||||||
getAuth,
|
getAuth,
|
||||||
GoogleAuthProvider,
|
GoogleAuthProvider,
|
||||||
linkWithPopup,
|
linkWithPopup,
|
||||||
|
OAuthCredential,
|
||||||
OAuthProvider,
|
OAuthProvider,
|
||||||
onAuthStateChanged,
|
onAuthStateChanged,
|
||||||
signInWithEmailAndPassword,
|
signInWithEmailAndPassword,
|
||||||
signInWithPopup,
|
signInWithPopup,
|
||||||
signOut,
|
signOut,
|
||||||
TwitterAuthProvider,
|
TwitterAuthProvider,
|
||||||
UserCredential
|
UserCredential,
|
||||||
} from "firebase/auth";
|
} from "firebase/auth";
|
||||||
import { app } from "./firebase";
|
import { app } from "./firebase";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
@@ -20,8 +23,12 @@ import { users } from "../db";
|
|||||||
import { doc, getDoc, setDoc } from "firebase/firestore";
|
import { doc, getDoc, setDoc } from "firebase/firestore";
|
||||||
import { servicesVersion } from "@ts-morph/common/lib/typescript";
|
import { servicesVersion } from "@ts-morph/common/lib/typescript";
|
||||||
import logger from "../util/logging";
|
import logger from "../util/logging";
|
||||||
|
import { debug } from "console";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
export default function useFirebaseAuth() {
|
export default function useFirebaseAuth() {
|
||||||
|
const [errorCredential, setErrorCredential] =
|
||||||
|
useState<OAuthCredential | null>();
|
||||||
const [profile, setProfile] = useState<Profile | undefined>();
|
const [profile, setProfile] = useState<Profile | undefined>();
|
||||||
const auth = getAuth(app);
|
const auth = getAuth(app);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -40,17 +47,23 @@ export default function useFirebaseAuth() {
|
|||||||
(savedProfile?.displayName || auth.currentUser.email) as string,
|
(savedProfile?.displayName || auth.currentUser.email) as string,
|
||||||
(savedProfile?.photoURL || auth.currentUser.email) as string,
|
(savedProfile?.photoURL || auth.currentUser.email) as string,
|
||||||
savedProfile?.about as string,
|
savedProfile?.about as string,
|
||||||
|
savedProfile?.mobileNumber as string,
|
||||||
new Date(),
|
new Date(),
|
||||||
savedProfile?.deviceRegistrations
|
savedProfile?.deviceRegistrations
|
||||||
);
|
);
|
||||||
setProfile(profile);
|
setProfile(profile);
|
||||||
await setDoc(doc(users, auth.currentUser.uid), Object.assign({}, profile), {
|
await setDoc(
|
||||||
merge: true
|
doc(users, auth.currentUser.uid),
|
||||||
});
|
Object.assign({}, profile),
|
||||||
|
{
|
||||||
|
merge: true,
|
||||||
|
}
|
||||||
|
);
|
||||||
return profile;
|
return profile;
|
||||||
}
|
}
|
||||||
}, [auth.currentUser]);
|
}, [auth.currentUser]);
|
||||||
const authStateChanged = useCallback(async (user: any) => {
|
const authStateChanged = useCallback(
|
||||||
|
async (user: any) => {
|
||||||
if (user) {
|
if (user) {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
const profile = await getUserProfile();
|
const profile = await getUserProfile();
|
||||||
@@ -70,10 +83,18 @@ export default function useFirebaseAuth() {
|
|||||||
|
|
||||||
const signUp = async (email: string, password: string): Promise<string> => {
|
const signUp = async (email: string, password: string): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
const response = await createUserWithEmailAndPassword(auth, email, password);
|
const response = await createUserWithEmailAndPassword(
|
||||||
|
auth,
|
||||||
|
email,
|
||||||
|
password
|
||||||
|
);
|
||||||
logger.debug("useFireBaseAuth", "signUp_success", response);
|
logger.debug("useFireBaseAuth", "signUp_success", response);
|
||||||
return "";
|
return "";
|
||||||
} catch (err: { code: string } | any) {
|
} catch (err: { code: string } | any) {
|
||||||
|
const credential = GoogleAuthProvider.credentialFromError(err);
|
||||||
|
if (credential) {
|
||||||
|
setErrorCredential(credential as OAuthCredential);
|
||||||
|
}
|
||||||
logger.error("useFireBaseAuth", "signUp", err);
|
logger.error("useFireBaseAuth", "signUp", err);
|
||||||
return err.code;
|
return err.code;
|
||||||
}
|
}
|
||||||
@@ -140,10 +161,30 @@ export default function useFirebaseAuth() {
|
|||||||
await _processSignIn(provider);
|
await _processSignIn(provider);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const linkAccounts = async (user: string, password: string) => {
|
||||||
|
debugger;
|
||||||
|
const credential = EmailAuthProvider.credential(user, password);
|
||||||
|
const provider = new GoogleAuthProvider();
|
||||||
|
const auth = getAuth();
|
||||||
|
if (!auth.currentUser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
linkWithPopup(auth.currentUser, provider)
|
||||||
|
.then((result) => {
|
||||||
|
// Accounts successfully linked.
|
||||||
|
debugger;
|
||||||
|
const credential = GoogleAuthProvider.credentialFromResult(result);
|
||||||
|
const user = result.user;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
logger.error("useFirebaseAuth", "linkWithPopup", error);
|
||||||
|
});
|
||||||
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = onAuthStateChanged(auth, authStateChanged);
|
const unsubscribe = onAuthStateChanged(auth, authStateChanged);
|
||||||
return unsubscribe;
|
return unsubscribe;
|
||||||
}, [auth, getUserProfile]);
|
}, [auth, authStateChanged]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
profile,
|
profile,
|
||||||
@@ -154,6 +195,7 @@ export default function useFirebaseAuth() {
|
|||||||
signInWithGoogle,
|
signInWithGoogle,
|
||||||
signInWithTwitter,
|
signInWithTwitter,
|
||||||
signInWithFacebook,
|
signInWithFacebook,
|
||||||
getUserProfile
|
linkAccounts,
|
||||||
|
getUserProfile,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
WithFieldValue,
|
WithFieldValue,
|
||||||
QueryDocumentSnapshot,
|
QueryDocumentSnapshot,
|
||||||
SnapshotOptions,
|
SnapshotOptions,
|
||||||
Timestamp
|
Timestamp,
|
||||||
} from "firebase/firestore";
|
} from "firebase/firestore";
|
||||||
|
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
@@ -17,7 +17,7 @@ const firebaseConfig = {
|
|||||||
storageBucket: "radio-otherway.appspot.com",
|
storageBucket: "radio-otherway.appspot.com",
|
||||||
messagingSenderId: "47147490249",
|
messagingSenderId: "47147490249",
|
||||||
appId: "1:47147490249:web:a84515b3ce1c481826e618",
|
appId: "1:47147490249:web:a84515b3ce1c481826e618",
|
||||||
measurementId: "G-12YB78EZM4"
|
measurementId: "G-12YB78EZM4",
|
||||||
};
|
};
|
||||||
export const firebaseApp = initializeApp(firebaseConfig);
|
export const firebaseApp = initializeApp(firebaseConfig);
|
||||||
const firestore = getFirestore();
|
const firestore = getFirestore();
|
||||||
@@ -30,7 +30,7 @@ const showConverter = {
|
|||||||
...show,
|
...show,
|
||||||
date: show.date
|
date: show.date
|
||||||
? Timestamp.fromDate(new Date(show.date as string))
|
? Timestamp.fromDate(new Date(show.date as string))
|
||||||
: new Date()
|
: new Date(),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
fromFirestore(
|
fromFirestore(
|
||||||
@@ -39,20 +39,44 @@ const showConverter = {
|
|||||||
): Show {
|
): Show {
|
||||||
const data = snapshot.data(options)!;
|
const data = snapshot.data(options)!;
|
||||||
return new Show(snapshot.id, data.title, data.date.toDate(), data.creator);
|
return new Show(snapshot.id, data.title, data.date.toDate(), data.creator);
|
||||||
}
|
},
|
||||||
|
};
|
||||||
|
const noticeConverter = {
|
||||||
|
toFirestore(notice: WithFieldValue<NotificationSchedule>): DocumentData {
|
||||||
|
return notice;
|
||||||
|
},
|
||||||
|
fromFirestore(
|
||||||
|
snapshot: QueryDocumentSnapshot,
|
||||||
|
options: SnapshotOptions
|
||||||
|
): NotificationSchedule {
|
||||||
|
const data = snapshot.data(options)!;
|
||||||
|
return new NotificationSchedule(
|
||||||
|
data.scheduleTimes.map((r: any) => r.toDate())
|
||||||
|
);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Import all your model types
|
// Import all your model types
|
||||||
import { Show, Reminder, RemindersProcessed, Profile } from "@/models";
|
import {
|
||||||
|
Show,
|
||||||
|
Reminder,
|
||||||
|
RemindersProcessed,
|
||||||
|
Profile,
|
||||||
|
NotificationSchedule,
|
||||||
|
} from "@/models";
|
||||||
// export all your collections
|
// export all your collections
|
||||||
|
|
||||||
export const users = createCollection<Profile>("users");
|
export const users = createCollection<Profile>("users");
|
||||||
export const shows =
|
export const shows =
|
||||||
createCollection<Show>("shows").withConverter(showConverter);
|
createCollection<Show>("shows").withConverter(showConverter);
|
||||||
|
export const notificationSchedules =
|
||||||
|
createCollection<NotificationSchedule>("noticeSchedules").withConverter(
|
||||||
|
noticeConverter
|
||||||
|
);
|
||||||
|
|
||||||
export const reminders = createCollection<Reminder>("reminders");
|
export const reminders = createCollection<Reminder>("reminders");
|
||||||
export const remindersProcessed =
|
export const remindersProcessed =
|
||||||
createCollection<RemindersProcessed>("reminders");
|
createCollection<RemindersProcessed>("reminders");
|
||||||
|
|
||||||
|
|
||||||
export default firestore;
|
export default firestore;
|
||||||
export { createCollection };
|
export { createCollection };
|
||||||
|
|||||||
15
src/lib/util/httpUtils.ts
Normal file
15
src/lib/util/httpUtils.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
import logger from "./logging";
|
||||||
|
|
||||||
|
const callWebHook = async (url: string, payload?: any): Promise<number> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post(url, payload);
|
||||||
|
|
||||||
|
return response.status;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("httpUtils", "callWebHook", err);
|
||||||
|
}
|
||||||
|
return 500;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { callWebHook };
|
||||||
@@ -1,24 +1,72 @@
|
|||||||
import pino from 'pino'
|
const logger = (() => {
|
||||||
import {logflarePinoVercel} from 'pino-logflare'
|
const checkIfLogsEnabled = () => {
|
||||||
|
if (process.browser) {
|
||||||
|
const search = global?.window?.location?.search;
|
||||||
|
const enabled =
|
||||||
|
search && new URLSearchParams(search).get("debug") === "true";
|
||||||
|
|
||||||
const {stream, send} = logflarePinoVercel({
|
global.areLogsEnabled = enabled || false;
|
||||||
apiKey: "MK3qgU_-pwHQ",
|
return global.areLogsEnabled;
|
||||||
sourceToken: "f7d8c11d-8f36-4981-8168-bfd69aa72bbf"
|
|
||||||
});
|
|
||||||
|
|
||||||
// create pino logger
|
|
||||||
const logger = pino({
|
|
||||||
browser: {
|
|
||||||
transmit: {
|
|
||||||
level: "info",
|
|
||||||
send: send,
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
level: "debug",
|
|
||||||
base: {
|
|
||||||
env: process.env.NODE_ENV,
|
|
||||||
revision: process.env.VERCEL_GITHUB_COMMIT_SHA,
|
|
||||||
},
|
|
||||||
}, stream);
|
|
||||||
|
|
||||||
export default logger
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDev = process.env.NODE_ENV !== "production";
|
||||||
|
|
||||||
|
const print = (type, ...messages) => {
|
||||||
|
if (typeof global.areLogsEnabled === "undefined") {
|
||||||
|
checkIfLogsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (global.areLogsEnabled || isDev) {
|
||||||
|
switch (type) {
|
||||||
|
case "info":
|
||||||
|
console.info(
|
||||||
|
"%c Custom Log:",
|
||||||
|
"background: blue; color: white;",
|
||||||
|
...messages
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "warn":
|
||||||
|
console.warn(
|
||||||
|
"%c Custom Log:",
|
||||||
|
"background: orange; color: white;",
|
||||||
|
...messages
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "error":
|
||||||
|
console.error(
|
||||||
|
"%c Custom Log:",
|
||||||
|
"background: red; color: white;",
|
||||||
|
...messages
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "trace":
|
||||||
|
console.trace(
|
||||||
|
"%c Custom Log:",
|
||||||
|
"background: grey; color: black;",
|
||||||
|
...messages
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "debug":
|
||||||
|
default:
|
||||||
|
console.log(
|
||||||
|
"%c Custom Log:",
|
||||||
|
"background: green; color: white;",
|
||||||
|
...messages
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
debug: print.bind(null, "debug"),
|
||||||
|
info: print.bind(null, "info"),
|
||||||
|
warn: print.bind(null, "warn"),
|
||||||
|
error: print.bind(null, "error"),
|
||||||
|
trace: print.bind(null, "trace"),
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
export default logger;
|
||||||
|
|||||||
@@ -1,16 +1,37 @@
|
|||||||
import logger from "@/lib/util/logging";
|
import logger from "@/lib/util/logging";
|
||||||
|
const twilio = require("twilio");
|
||||||
|
|
||||||
const sendSMS = async (number: string, body: string) => {
|
const sendSMS = async (number: string, body: string) => {
|
||||||
const twilio = require("twilio");
|
const client = require("twilio")(
|
||||||
const client = require("twilio")(process.env.TWILIO_SID, process.env.TWILIO_AUTH_TOKEN);
|
process.env.TWILIO_SID,
|
||||||
|
process.env.TWILIO_AUTH_TOKEN
|
||||||
|
);
|
||||||
|
|
||||||
const message = await client.messages
|
const message = await client.messages.create({
|
||||||
.create({
|
body: body,
|
||||||
body: body,
|
to: number, // Text this number
|
||||||
to: number, // Text this number
|
from: process.env.TWILIO_FROM, // From a valid Twilio number
|
||||||
from: process.env.TWILIO_FROM // From a valid Twilio number
|
});
|
||||||
});
|
|
||||||
logger.debug(`SMS sent to ${number}`, message);
|
logger.debug(`SMS sent to ${number}`, message);
|
||||||
};
|
};
|
||||||
|
const sendWhatsApp = async (number: string, body: string): Promise<boolean> => {
|
||||||
|
const client = require("twilio")(
|
||||||
|
process.env.TWILIO_SID,
|
||||||
|
process.env.TWILIO_AUTH_TOKEN
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
const message = await client.messages.create({
|
||||||
|
body: body,
|
||||||
|
from: `whatsapp:${process.env.WHATSAPP_NUMBER}`,
|
||||||
|
to: `whatsapp:${number}`,
|
||||||
|
});
|
||||||
|
logger.debug(`WhatsApp sent to ${number}`, message);
|
||||||
|
logger.debug("sms", "messageSent", message.sid);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
logger.error("sms", "message failure", err);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
export { sendSMS };
|
export { sendSMS, sendWhatsApp };
|
||||||
|
|||||||
4
src/lib/util/objectUtils.ts
Normal file
4
src/lib/util/objectUtils.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
const removeUndefinedProperties = (obj: any) => {
|
||||||
|
return JSON.parse(JSON.stringify(obj));
|
||||||
|
};
|
||||||
|
export { removeUndefinedProperties };
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
export default interface DeviceRegistration {
|
export default class DeviceRegistration {
|
||||||
deviceType: string;
|
deviceType: string;
|
||||||
fcmToken: string;
|
fcmToken: string;
|
||||||
lastSeen: Date;
|
lastSeen: Date;
|
||||||
};
|
constructor(deviceType: string, fcmToken: string, lastSeen: Date) {
|
||||||
|
this.deviceType = deviceType;
|
||||||
|
this.fcmToken = fcmToken;
|
||||||
|
this.lastSeen = lastSeen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import Reminder from "./reminder";
|
|||||||
import Notification from "./notification";
|
import Notification from "./notification";
|
||||||
import DeviceRegistration from "./deviceregistration";
|
import DeviceRegistration from "./deviceregistration";
|
||||||
import type { RemindersProcessed } from "./processes";
|
import type { RemindersProcessed } from "./processes";
|
||||||
|
import NotificationSchedule from "./notificationSchedule";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Profile,
|
Profile,
|
||||||
Show,
|
Show,
|
||||||
Reminder,
|
Reminder,
|
||||||
Notification,
|
Notification,
|
||||||
type DeviceRegistration,
|
DeviceRegistration,
|
||||||
|
NotificationSchedule,
|
||||||
RemindersProcessed
|
RemindersProcessed
|
||||||
};
|
};
|
||||||
|
|||||||
7
src/models/notificationSchedule.ts
Normal file
7
src/models/notificationSchedule.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export default class NotificationSchedule {
|
||||||
|
scheduleTimes: Date[];
|
||||||
|
|
||||||
|
constructor(scheduleTimes: Date[]) {
|
||||||
|
this.scheduleTimes = scheduleTimes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,20 +2,23 @@ import DeviceRegistration from "@/models/deviceregistration";
|
|||||||
|
|
||||||
export default class Profile {
|
export default class Profile {
|
||||||
id: string;
|
id: string;
|
||||||
email: string | null;
|
email?: string;
|
||||||
displayName: string | null;
|
displayName?: string;
|
||||||
photoURL: string | null;
|
photoURL?: string;
|
||||||
|
headerPhotoURL?: string;
|
||||||
emailVerified: boolean = false;
|
emailVerified: boolean = false;
|
||||||
about?: String;
|
about?: string;
|
||||||
|
mobileNumber?: string;
|
||||||
lastSeen: Date;
|
lastSeen: Date;
|
||||||
deviceRegistrations?: DeviceRegistration[] = [];
|
deviceRegistrations?: DeviceRegistration[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
id: string,
|
id: string,
|
||||||
email: string | null,
|
email?: string,
|
||||||
displayName: string | null,
|
displayName?: string,
|
||||||
photoURL: string | null,
|
photoURL?: string,
|
||||||
about?: string,
|
about?: string,
|
||||||
|
mobileNumber?: string,
|
||||||
lastSeen?: Date,
|
lastSeen?: Date,
|
||||||
deviceRegistrations?: DeviceRegistration[]
|
deviceRegistrations?: DeviceRegistration[]
|
||||||
) {
|
) {
|
||||||
@@ -24,6 +27,7 @@ export default class Profile {
|
|||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
this.photoURL = photoURL;
|
this.photoURL = photoURL;
|
||||||
this.about = about || "";
|
this.about = about || "";
|
||||||
|
this.mobileNumber = mobileNumber || "";
|
||||||
|
|
||||||
this.lastSeen = lastSeen || new Date();
|
this.lastSeen = lastSeen || new Date();
|
||||||
this.deviceRegistrations = deviceRegistrations || this.deviceRegistrations;
|
this.deviceRegistrations = deviceRegistrations || this.deviceRegistrations;
|
||||||
|
|||||||
@@ -5,33 +5,56 @@ import { doc, setDoc } from "@firebase/firestore";
|
|||||||
import { shows } from "@/lib/db";
|
import { shows } from "@/lib/db";
|
||||||
import { Show } from "@/models";
|
import { Show } from "@/models";
|
||||||
import Settings from "@/lib/db/settings";
|
import Settings from "@/lib/db/settings";
|
||||||
|
import { notificationSchedules } from "@/lib/db/index";
|
||||||
|
import { addSeconds } from "@/lib/util/dateUtils";
|
||||||
|
import { callWebHook } from "@/lib/util/httpUtils";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
try {
|
try {
|
||||||
logger.debug("API:cache", "Starting cache of events");
|
logger.debug("Starting sync of shows from google calendar");
|
||||||
const syncToken = await Settings.read("CalendarSyncToken");
|
const syncToken = await Settings.read("CalendarSyncToken");
|
||||||
logger.debug("API:cache", "Sync token", syncToken);
|
|
||||||
const e = await getCalendarEntries(syncToken);
|
const e = await getCalendarEntries(syncToken);
|
||||||
logger.debug("API:cache", "Got events", e?.events);
|
|
||||||
if (!e?.events) {
|
if (!e?.events) {
|
||||||
res.status(204).json({ result: "No calendar entries found" });
|
res
|
||||||
|
.status(StatusCodes.NO_CONTENT)
|
||||||
|
.json({ result: "No calendar entries found" });
|
||||||
} else {
|
} else {
|
||||||
const entries = e?.events.map((r: any) => Show.fromJson(r));
|
const entries: Show[] = e?.events.map((r: any) => Show.fromJson(r));
|
||||||
logger.debug("API:cache", "Mapping events", entries);
|
|
||||||
for (const entry of entries) {
|
for (const entry of entries) {
|
||||||
logger.debug("API:cache", "Mapping event", entry);
|
|
||||||
const showRef = doc(shows, entry.id);
|
const showRef = doc(shows, entry.id);
|
||||||
await setDoc(showRef, {
|
await setDoc(
|
||||||
title: entry.title,
|
showRef,
|
||||||
date: entry.date,
|
{
|
||||||
creator: entry.creator
|
title: entry.title,
|
||||||
}, { merge: true });
|
date: entry.date,
|
||||||
|
creator: entry.creator,
|
||||||
|
},
|
||||||
|
{ merge: true }
|
||||||
|
);
|
||||||
|
const schedules = [
|
||||||
|
addSeconds(new Date(entry.date), -3600), //an hour before
|
||||||
|
addSeconds(new Date(entry.date), 0), //on time
|
||||||
|
];
|
||||||
|
const notificationSchedulRef = await setDoc(
|
||||||
|
doc(notificationSchedules, entry.id),
|
||||||
|
Object.assign({}, { scheduleTimes: schedules }),
|
||||||
|
{ merge: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
logger.debug("Stored show", res);
|
|
||||||
if (e?.syncToken) {
|
if (e?.syncToken) {
|
||||||
await Settings.write("CalendarSyncToken", e?.syncToken);
|
await Settings.write("CalendarSyncToken", e?.syncToken);
|
||||||
}
|
}
|
||||||
res.status(200).json({ status: "OK", entries });
|
//ping the scheduler to let it know stuff has changed
|
||||||
|
if (entries.length > 0) {
|
||||||
|
const result = await callWebHook(
|
||||||
|
`${process.env.SCHEDULER_API_HOST}/job/reload`
|
||||||
|
);
|
||||||
|
if (result !== 200) {
|
||||||
|
logger.error("cache", "Unable to notify job scheduler");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.status(StatusCodes.OK).json({ status: "OK", entries });
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error(err);
|
logger.error(err);
|
||||||
|
|||||||
36
src/pages/api/cron/notify.ts
Normal file
36
src/pages/api/cron/notify.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import { shows, users } from "@/lib/db";
|
||||||
|
import { Show } from "@/models";
|
||||||
|
import { doc, getDocs } from "@firebase/firestore";
|
||||||
|
import { getDoc, query, where } from "firebase/firestore";
|
||||||
|
import { sendWhatsApp } from "@/lib/util/notifications/sms";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
|
||||||
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
return res.status(StatusCodes.METHOD_NOT_ALLOWED);
|
||||||
|
}
|
||||||
|
const { showId } = req.body;
|
||||||
|
|
||||||
|
const q = await getDoc(doc(shows, showId));
|
||||||
|
const show = Show.fromJson(q.data());
|
||||||
|
|
||||||
|
//iterate through every user and get their notifications
|
||||||
|
//and send 'em off
|
||||||
|
|
||||||
|
//TODO: Move this out of here
|
||||||
|
const usersQuery = await getDocs(
|
||||||
|
query(users, where("mobileNumber", "!=", null))
|
||||||
|
);
|
||||||
|
usersQuery.forEach(async (u) => {
|
||||||
|
const user = u.data();
|
||||||
|
if (user.mobileNumber) {
|
||||||
|
console.log("notify", "sending notification to ", user);
|
||||||
|
const message = `New show from ${show.creator} starting now!!\nhttps://mixcloud.com/live/radiootherway`;
|
||||||
|
await sendWhatsApp(user.mobileNumber, message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.status(StatusCodes.OK).json({ status: "OK" });
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
export default handler;
|
||||||
@@ -47,7 +47,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.status(200).json({status: "OK"});
|
res.status(StatusCodes.OK).json({status: "OK"});
|
||||||
res.end();
|
res.end();
|
||||||
};
|
};
|
||||||
export default handler;
|
export default handler;
|
||||||
|
|||||||
@@ -5,42 +5,43 @@ import { doc, getDocs, query, where } from "@firebase/firestore";
|
|||||||
import { getDoc } from "firebase/firestore";
|
import { getDoc } from "firebase/firestore";
|
||||||
import { users } from "@/lib/db";
|
import { users } from "@/lib/db";
|
||||||
import { Profile, Reminder } from "@/models";
|
import { Profile, Reminder } from "@/models";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
|
||||||
const _getPayload = (message: string) => ({
|
const _getPayload = (message: string) => ({
|
||||||
notification: {
|
notification: {
|
||||||
title: "Argle Bargle",
|
title: "Argle Bargle",
|
||||||
body: `You are a ${message}`,
|
body: `You are a ${message}`,
|
||||||
image: "https://otherway.fergl.ie/logo.png"
|
image: "https://otherway.fergl.ie/logo.png",
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
url: "https://google.com"
|
url: "https://google.com",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
|
||||||
|
|
||||||
// Send a message to the device corresponding to the provided
|
// Send a message to the device corresponding to the provided
|
||||||
// registration token.
|
// registration token.
|
||||||
try {
|
try {
|
||||||
const results: any[] = [];
|
const results: any[] = [];
|
||||||
const userResultSet = await getDocs(query(users, where("email", "==", "fergal.moran@gmail.com")));
|
const userResultSet = await getDocs(
|
||||||
|
query(users, where("email", "==", "fergal.moran@gmail.com"))
|
||||||
|
);
|
||||||
for (let u of userResultSet.docs) {
|
for (let u of userResultSet.docs) {
|
||||||
const user = u.data() as Profile;
|
const user = u.data() as Profile;
|
||||||
if (user?.deviceRegistrations) {
|
if (user?.deviceRegistrations) {
|
||||||
for (const token of user?.deviceRegistrations) {
|
for (const token of user?.deviceRegistrations) {
|
||||||
results.push(await firebaseAdmin
|
results.push(
|
||||||
.messaging()
|
await firebaseAdmin
|
||||||
.sendToDevice(
|
.messaging()
|
||||||
token.fcmToken,
|
.sendToDevice(token.fcmToken, _getPayload(token.deviceType))
|
||||||
_getPayload(token.deviceType)));
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res.status(200).json({ results });
|
res.status(StatusCodes.OK).json({ results });
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error("fcm", "sendPush", "Failed", err);
|
logger.error("fcm", "sendPush", "Failed", err);
|
||||||
res.status(500);
|
res.status(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||||
}
|
}
|
||||||
res.end();
|
res.end();
|
||||||
};
|
};
|
||||||
|
|||||||
6
src/pages/api/debug/index.ts
Normal file
6
src/pages/api/debug/index.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
res.status(StatusCodes.OK).end();
|
||||||
|
}
|
||||||
23
src/pages/api/debug/message/index.ts
Normal file
23
src/pages/api/debug/message/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { sendWhatsApp } from "@/lib/util/notifications/sms";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
|
export default async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse
|
||||||
|
) {
|
||||||
|
if (req.method !== "POST") {
|
||||||
|
return res.status(StatusCodes.METHOD_NOT_ALLOWED);
|
||||||
|
}
|
||||||
|
const { number, message } = req.body;
|
||||||
|
if (number && message) {
|
||||||
|
const result = await sendWhatsApp(number, message);
|
||||||
|
if (result) res.status(StatusCodes.OK);
|
||||||
|
else {
|
||||||
|
res.status(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.status(StatusCodes.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
}
|
||||||
@@ -1,23 +1,28 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from "next";
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { reminders, shows } from "@/lib/db";
|
import { reminders } from "@/lib/db";
|
||||||
import { addDoc, doc, setDoc } from "@firebase/firestore";
|
import { doc, setDoc } from "@firebase/firestore";
|
||||||
|
import { StatusCodes } from "http-status-codes";
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
if (req.method === "POST") {
|
if (req.method !== "POST") {
|
||||||
|
res.status(StatusCodes.METHOD_NOT_ALLOWED);
|
||||||
|
} else {
|
||||||
const { userId, showId } = req.body;
|
const { userId, showId } = req.body;
|
||||||
const docKey = `${userId}_${showId}`;
|
const docKey = `${userId}_${showId}`;
|
||||||
const remindersRef = doc(reminders, docKey);
|
const remindersRef = doc(reminders, docKey);
|
||||||
await setDoc(remindersRef, {
|
await setDoc(
|
||||||
userId,
|
remindersRef,
|
||||||
showId,
|
{
|
||||||
notifications: [
|
userId,
|
||||||
{ secondsBefore: 60 * 60, destination: "353868065119" } //just set a single reminder for an hour beforehand
|
showId,
|
||||||
]
|
notifications: [
|
||||||
}, { merge: true });
|
{ secondsBefore: 60 * 60, destination: "353868065119" }, //just set a single reminder for an hour beforehand
|
||||||
res.status(201);
|
],
|
||||||
} else {
|
},
|
||||||
res.status(405);
|
{ merge: true }
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
res.status(StatusCodes.CREATED);
|
||||||
res.end();
|
res.end();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
creator: entry.creator
|
creator: entry.creator
|
||||||
}, { merge: true });
|
}, { merge: true });
|
||||||
}
|
}
|
||||||
res.status(200).json({ result: "We got pinged" });
|
res.status(StatusCodes.OK).json({ result: "We got pinged" });
|
||||||
res.end();
|
res.end();
|
||||||
};
|
};
|
||||||
|
|||||||
16
src/pages/api/shows/notifications.ts
Normal file
16
src/pages/api/shows/notifications.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import db, { notificationSchedules } from "@/lib/db";
|
||||||
|
import { getDocs, query, where } from "@firebase/firestore";
|
||||||
|
|
||||||
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
const q = query(notificationSchedules);
|
||||||
|
const upcoming = await getDocs(q);
|
||||||
|
res.status(StatusCodes.OK).json(
|
||||||
|
upcoming.docs.map((r) => ({
|
||||||
|
showId: r.id,
|
||||||
|
...r.data(),
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
res.end();
|
||||||
|
};
|
||||||
|
export default handler;
|
||||||
@@ -8,7 +8,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
where("date", ">", new Date())
|
where("date", ">", new Date())
|
||||||
);
|
);
|
||||||
const upcoming = await getDocs(q);
|
const upcoming = await getDocs(q);
|
||||||
res.status(200).json(upcoming.docs.map(r => r.data()));
|
res.status(StatusCodes.OK).json(upcoming.docs.map(r => r.data()));
|
||||||
res.end();
|
res.end();
|
||||||
};
|
};
|
||||||
export default handler;
|
export default handler;
|
||||||
|
|||||||
@@ -2,6 +2,6 @@ import { NextApiRequest, NextApiResponse } from "next";
|
|||||||
import { setupCalendarWebhook } from "@/lib/util/google/calendarWatcher";
|
import { setupCalendarWebhook } from "@/lib/util/google/calendarWatcher";
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
res.status(200).json({ hello: process.env });
|
res.status(StatusCodes.OK).json({ hello: process.env });
|
||||||
};
|
};
|
||||||
export default handler;
|
export default handler;
|
||||||
|
|||||||
298
yarn.lock
298
yarn.lock
@@ -560,21 +560,21 @@
|
|||||||
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
|
||||||
|
|
||||||
"@jsdoc/salty@^0.2.1":
|
"@jsdoc/salty@^0.2.1":
|
||||||
version "0.2.3"
|
version "0.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/@jsdoc/salty/-/salty-0.2.3.tgz#aab70c8756c1b98598bbc30867d3aa7a31b5c7d4"
|
resolved "https://registry.yarnpkg.com/@jsdoc/salty/-/salty-0.2.4.tgz#049d92e1814b7fdffde2564ecd5746fb46278d8c"
|
||||||
integrity sha512-bbtCxCkxcnWhi50I+4Lj6mdz9w3pOXOgEQrID8TCZ/DF51fW7M9GCQW2y45SpBDdHd1Eirm1X/Cf6CkAAe8HPg==
|
integrity sha512-HRBmslXHM6kpZOfGf0o41NUlGYGER0NoUBcT2Sik4rxzAN7f7+si7ad57SFSFpftvaMVnUaY7YlJuv3v5G80ZA==
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
"@next/env@13.2.1":
|
"@next/env@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.1.tgz#082d42cfc0c794e9185d7b4133d71440ba2e795d"
|
resolved "https://registry.yarnpkg.com/@next/env/-/env-13.2.2.tgz#52761a775ad9cf241a6973bd72afba6ba10a4e0c"
|
||||||
integrity sha512-Hq+6QZ6kgmloCg8Kgrix+4F0HtvLqVK3FZAnlAoS0eonaDemHe1Km4kwjSWRE3JNpJNcKxFHF+jsZrYo0SxWoQ==
|
integrity sha512-sBcFEJS8j2cNQemYy07TKUd8lSWj3/mzFA4GCTr/4T4LfYiw5Ep+PZ06AuFdR3z+jIZt9YqaXwUYi1J4p4yABQ==
|
||||||
|
|
||||||
"@next/eslint-plugin-next@13.1.5":
|
"@next/eslint-plugin-next@13.2.2":
|
||||||
version "13.1.5"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.1.5.tgz#3eebea0835d2ec549a7b4b8976bf1d7796cca632"
|
resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.2.2.tgz#410ea50276e0408c7565b499de702a274d6dd301"
|
||||||
integrity sha512-3kvLTX35bOWOCKU8KY74Ygczc55Qk/kB2TQy0tH7Rti6hzZ6Aij7WQ8zHdIVjmnlD0n/zXWXrIf5y56RKcLQkQ==
|
integrity sha512-Kp4ThmQ4f5AybCB0RgxOWmKmAQmTj7/K9BT+ZzgHRJ25h3klO+MRSKC8yqiDcgl3qNVIjKwZi8D/thQGE7o3Mg==
|
||||||
dependencies:
|
dependencies:
|
||||||
glob "7.1.7"
|
glob "7.1.7"
|
||||||
|
|
||||||
@@ -583,70 +583,70 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@next/font/-/font-13.2.0.tgz#677b3fc67243bccbae94d7d1e96a494be98206c4"
|
resolved "https://registry.yarnpkg.com/@next/font/-/font-13.2.0.tgz#677b3fc67243bccbae94d7d1e96a494be98206c4"
|
||||||
integrity sha512-pWoegIxqegV+9+gFmRCZao6xhA2m3kKS34lMXqShJ5ibRuyHkP/tfDE82LzYZmVQ3p51ZrkwTugNwy/ohiE2cA==
|
integrity sha512-pWoegIxqegV+9+gFmRCZao6xhA2m3kKS34lMXqShJ5ibRuyHkP/tfDE82LzYZmVQ3p51ZrkwTugNwy/ohiE2cA==
|
||||||
|
|
||||||
"@next/swc-android-arm-eabi@13.2.1":
|
"@next/swc-android-arm-eabi@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.1.tgz#67f2580fbbe05ee006220688972c5e3a555fc741"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.2.2.tgz#84561ccb52736a97fea82ce001f833eab4310115"
|
||||||
integrity sha512-Yua7mUpEd1wzIT6Jjl3dpRizIfGp9NR4F2xeRuQv+ae+SDI1Em2WyM9m46UL+oeW5GpMiEHoaBagr47RScZFmQ==
|
integrity sha512-JHTnsNTl9gDQkWqggJFj6rQHK2+9lIMGolOPihfZBQAE48amVsGkYyFHbiuWnhwnYX99fCFWomARDwOtLAhzdQ==
|
||||||
|
|
||||||
"@next/swc-android-arm64@13.2.1":
|
"@next/swc-android-arm64@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.1.tgz#460a02b69eb23bb5f402266bcea9cadae59415c1"
|
resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.2.2.tgz#aebb9664e7acf9996e02c7619ef536976a3b6bbb"
|
||||||
integrity sha512-Bifcr2f6VwInOdq1uH/9lp8fH7Nf7XGkIx4XceVd32LPJqG2c6FZU8ZRBvTdhxzXVpt5TPtuXhOP4Ij9UPqsVw==
|
integrity sha512-j3zfjrojWuejU7bjucwBFviN9hQkQ1HqRVYMp8PDX82LXMc2pV4uZ3e9jQ8qk3OWsA1m+luaBK8o46NvS/Tetg==
|
||||||
|
|
||||||
"@next/swc-darwin-arm64@13.2.1":
|
"@next/swc-darwin-arm64@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.1.tgz#8b8530ff417802027471aee2419f78a58a863ccb"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.2.2.tgz#233b450f92b0ed5f2c673666973c9165b8a82883"
|
||||||
integrity sha512-gvqm+fGMYxAkwBapH0Vvng5yrb6HTkIvZfY4oEdwwYrwuLdkjqnJygCMgpNqIFmAHSXgtlWxfYv1VC8sjN81Kw==
|
integrity sha512-X66dKAq6IBmkirnsWyvncJ9i3kWkE2WrRbXGoFoEPNoyjgtV2qyLyk4ET9JIS0TJ2u01qODML6j4pKeqK8rNLA==
|
||||||
|
|
||||||
"@next/swc-darwin-x64@13.2.1":
|
"@next/swc-darwin-x64@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.1.tgz#80aebb3329a1e4568a28de1ee177780b3d50330c"
|
resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.2.2.tgz#5d86717ab0b76072dd04d6e516eda0d5c510268f"
|
||||||
integrity sha512-HGqVqmaZWj6zomqOZUVbO5NhlABL0iIaxTmd0O5B0MoMa5zpDGoaHSG+fxgcWMXcGcxmUNchv1NfNOYiTKoHOg==
|
integrity sha512-jsJKzVLlCNrG/Ot9Owv8QWUymAyS+Hcf/KQsXOYtL6PgvGqBMJ931SdaBpLOuyRD+sozTIRiAPCDwVgF/62PBQ==
|
||||||
|
|
||||||
"@next/swc-freebsd-x64@13.2.1":
|
"@next/swc-freebsd-x64@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.1.tgz#250ea2ab7e1734f22d11c677c463fab9ac33a516"
|
resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.2.2.tgz#0caecb595eb63fe43d3346577116d5de7c19b9f1"
|
||||||
integrity sha512-N/a4JarAq+E+g+9K2ywJUmDIgU2xs2nA+BBldH0oq4zYJMRiUhL0iaN9G4e72VmGOJ61L/3W6VN8RIUOwTLoqQ==
|
integrity sha512-oToMAjhdGWSgSOimKJ9GwPxOqoBIbbVUKoFGP5Imy5UGbbsnyV2jSLGhoi2/dH1Ko5X5d1R1la/X05xNB/p6ag==
|
||||||
|
|
||||||
"@next/swc-linux-arm-gnueabihf@13.2.1":
|
"@next/swc-linux-arm-gnueabihf@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.1.tgz#fe6bb29ed348a5f8ecae3740df22a8d8130c474a"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.2.2.tgz#db9578676d9dfd0e1e56070229d4453adf931f58"
|
||||||
integrity sha512-WaFoerF/eRbhbE57TaIGJXbQAERADZ/RZ45u6qox9beb5xnWsyYgzX+WuN7Tkhyvga0/aMuVYFzS9CEay7D+bw==
|
integrity sha512-DSgXP2kkLt+oM3fRWaa2xPAUmtTnoa+GBfV5Czbgv4htY1BKClobp7Bj909TZUswvxXJtXiW8GrJp2To5lQfmw==
|
||||||
|
|
||||||
"@next/swc-linux-arm64-gnu@13.2.1":
|
"@next/swc-linux-arm64-gnu@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.1.tgz#4781b927fc5e421f3cea2b29e5d38e5e4837b198"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.2.2.tgz#7c6a3832516b836d0610f6591b6522f318b725f9"
|
||||||
integrity sha512-R+Jhc1/RJTnncE9fkePboHDNOCm1WJ8daanWbjKhfPySMyeniKYRwGn5SLYW3S8YlRS0QVdZaaszDSZWgUcsmA==
|
integrity sha512-HR1Nf7kz5NmijCiArJTz4bjJlbWyPA3JQ3SbxnPXDMPUkECBG1p0z6y2N2+oqNouMyOriNR6TSEgh7F2tASdZg==
|
||||||
|
|
||||||
"@next/swc-linux-arm64-musl@13.2.1":
|
"@next/swc-linux-arm64-musl@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.1.tgz#c2ba0a121b0255ba62450916bc70e6d0e26cbc98"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.2.2.tgz#540b6a81f873e7bce9460e51d30aeef7a7c31199"
|
||||||
integrity sha512-oI1UfZPidGAVddlL2eOTmfsuKV9EaT1aktIzVIxIAgxzQSdwsV371gU3G55ggkurzfdlgF3GThFePDWF0d8dmw==
|
integrity sha512-hDf08/yPZisKQ19lMHkVuQUmipmSq9QPzFmNDcV7e4QazTbIRwCC8J605EHedg+RGHS78CUwVhGQoQ2mbQ5y3w==
|
||||||
|
|
||||||
"@next/swc-linux-x64-gnu@13.2.1":
|
"@next/swc-linux-x64-gnu@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.1.tgz#573c220f8b087e5d131d1fba58d3e1a670b220ad"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.2.2.tgz#daa394a4f288abd98cf535e1b0137322364bd1c7"
|
||||||
integrity sha512-PCygPwrQmS+7WUuAWWioWMZCzZm4PG91lfRxToLDg7yIm/3YfAw5N2EK2TaM9pzlWdvHQAqRMX/oLvv027xUiA==
|
integrity sha512-9QeTU1YVhQXoVUOBXErJoExZR3XTovplMCTwB+RAtHu3oUGGs4KQFY8MoFUOmMA7CAJnKS+h1MD1T9/HDpS9PQ==
|
||||||
|
|
||||||
"@next/swc-linux-x64-musl@13.2.1":
|
"@next/swc-linux-x64-musl@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.1.tgz#950b5bb920b322ca7b447efbd12a9c7a10c3a642"
|
resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.2.2.tgz#056c2fcd008e3302d829dfa739a0f017b4a01e5a"
|
||||||
integrity sha512-sUAKxo7CFZYGHNxheGh9nIBElLYBM6md/liEGfOTwh/xna4/GTTcmkGWkF7PdnvaYNgcPIQgHIMYiAa6yBKAVw==
|
integrity sha512-SvyejahQLY79YJFXU27AcoDbmRqdK08t7HAf5LmocLdR5fxIneg7Oim/pwcuS/w8pYqDSAiCiMO0AAxSfO5jZQ==
|
||||||
|
|
||||||
"@next/swc-win32-arm64-msvc@13.2.1":
|
"@next/swc-win32-arm64-msvc@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.1.tgz#dbff3c4f5a3812a7059dac05804148a0f98682db"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.2.2.tgz#3f188da59eef2ef1c99b695ea3c68cb3cb3f971f"
|
||||||
integrity sha512-qDmyEjDBpl/vBXxuOOKKWmPQOcARcZIMach1s7kjzaien0SySut/PHRlj56sosa81Wt4hTGhfhZ1R7g1n7+B8w==
|
integrity sha512-Av46kUBp1qtJdk/HrIHZMIARE+jcMSJLHcrs/MkbHR1p4iEXf3JkZRrdQDUKZaz/VHDYc7Qw+vUv7zmgPZJbmw==
|
||||||
|
|
||||||
"@next/swc-win32-ia32-msvc@13.2.1":
|
"@next/swc-win32-ia32-msvc@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.1.tgz#7d2c17be7b8d9963984f5c15cc2588127101f620"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.2.2.tgz#5873d4a7c29aff1ac75bea1f651fa9a332abb8a2"
|
||||||
integrity sha512-2joqFQ81ZYPg6DcikIzQn3DgjKglNhPAozx6dL5sCNkr1CPMD0YIkJgT3CnYyMHQ04Qi3Npv0XX3MD6LJO8OCA==
|
integrity sha512-6HvAJY7yYfU+Ast3GPM8O+BsLGAdhZV0qUwHerJo5+Yzzx0VXW+OZTJ9O6RU01HBiMiqdz7SF3Or4Vca6WZKkQ==
|
||||||
|
|
||||||
"@next/swc-win32-x64-msvc@13.2.1":
|
"@next/swc-win32-x64-msvc@13.2.2":
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.1.tgz#09713c6a925461f414e89422851326d1625bd4d2"
|
resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.2.2.tgz#72c77c67eeafb393fbc744d530d763e5c356e0b5"
|
||||||
integrity sha512-r3+0fSaIZT6N237iMzwUhfNwjhAFvXjqB+4iuW+wcpxW+LHm1g/IoxN8eSRcb8jPItC86JxjAxpke0QL97qd6g==
|
integrity sha512-7oNoiepUMf43+/4kUdpeGGkEBd3bOQwzo4O+bq/jslHb1IrQ7b8ZF1ODQyrDd661um6q5D+0sOtyRZJpo8e1jQ==
|
||||||
|
|
||||||
"@nodelib/fs.scandir@2.1.5":
|
"@nodelib/fs.scandir@2.1.5":
|
||||||
version "2.1.5"
|
version "2.1.5"
|
||||||
@@ -682,21 +682,21 @@
|
|||||||
tslib "^2.4.0"
|
tslib "^2.4.0"
|
||||||
|
|
||||||
"@prisma/client@^4.9.0":
|
"@prisma/client@^4.9.0":
|
||||||
version "4.10.1"
|
version "4.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.10.1.tgz#c47fd54661ee74b174cee63e9dc418ecf57a6ccd"
|
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-4.11.0.tgz#41d5664dea4172c954190a432f70b86d3e2e629b"
|
||||||
integrity sha512-VonXLJZybdt8e5XZH5vnIGCRNnIh6OMX1FS3H/yzMGLT3STj5TJ/OkMcednrvELgk8PK89Vo3aSh51MWNO0axA==
|
integrity sha512-0INHYkQIqgAjrt7NzhYpeDQi8x3Nvylc2uDngKyFDDj1tTRQ4uV1HnVmd1sQEraeVAN63SOK0dgCKQHlvjL0KA==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/engines-version" "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19"
|
"@prisma/engines-version" "4.11.0-57.8fde8fef4033376662cad983758335009d522acb"
|
||||||
|
|
||||||
"@prisma/engines-version@4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19":
|
"@prisma/engines-version@4.11.0-57.8fde8fef4033376662cad983758335009d522acb":
|
||||||
version "4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19"
|
version "4.11.0-57.8fde8fef4033376662cad983758335009d522acb"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.10.1-2.aead147aa326ccb985dcfed5b065b4fdabd44b19.tgz#312359d9d00e39e323136d0270876293d315658e"
|
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-4.11.0-57.8fde8fef4033376662cad983758335009d522acb.tgz#74af5ff56170c78e93ce46c56510160f58cd3c01"
|
||||||
integrity sha512-tsjTho7laDhf9EJ9EnDxAPEf7yrigSMDhniXeU4YoWc7azHAs4GPxRi2P9LTFonmHkJLMOLjR77J1oIP8Ife1w==
|
integrity sha512-3Vd8Qq06d5xD8Ch5WauWcUUrsVPdMC6Ge8ILji8RFfyhUpqon6qSyGM0apvr1O8n8qH8cKkEFqRPsYjuz5r83g==
|
||||||
|
|
||||||
"@prisma/engines@4.10.1":
|
"@prisma/engines@4.11.0":
|
||||||
version "4.10.1"
|
version "4.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.10.1.tgz#c7062747f254e5d5fce98a8cae566c25f9f29fb2"
|
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-4.11.0.tgz#c99749bfe20f58e8f4d2b5e04fee0785eba440e1"
|
||||||
integrity sha512-B3tcTxjx196nuAu1GOTKO9cGPUgTFHYRdkPkTS4m5ptb2cejyBlH9X7GOfSt3xlI7p4zAJDshJP4JJivCg9ouA==
|
integrity sha512-0AEBi2HXGV02cf6ASsBPhfsVIbVSDC9nbQed4iiY5eHttW9ZtMxHThuKZE1pnESbr8HRdgmFSa/Kn4OSNYuibg==
|
||||||
|
|
||||||
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
@@ -1201,6 +1201,11 @@ async-retry@^1.3.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
retry "0.13.1"
|
retry "0.13.1"
|
||||||
|
|
||||||
|
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==
|
||||||
|
|
||||||
atomic-sleep@^1.0.0:
|
atomic-sleep@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b"
|
resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b"
|
||||||
@@ -1247,6 +1252,15 @@ axios@^0.26.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
follow-redirects "^1.14.8"
|
follow-redirects "^1.14.8"
|
||||||
|
|
||||||
|
axios@^1.3.4:
|
||||||
|
version "1.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024"
|
||||||
|
integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "^1.15.0"
|
||||||
|
form-data "^4.0.0"
|
||||||
|
proxy-from-env "^1.1.0"
|
||||||
|
|
||||||
axobject-query@^3.1.1:
|
axobject-query@^3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1"
|
resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-3.1.1.tgz#3b6e5c6d4e43ca7ba51c5babf99d22a9c68485e1"
|
||||||
@@ -1421,7 +1435,7 @@ chokidar@^3.5.3:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
classnames@^2.2.5, classnames@^2.3.2:
|
classnames@^2.2.5, classnames@^2.3.1, classnames@^2.3.2:
|
||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924"
|
||||||
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
|
||||||
@@ -1490,6 +1504,13 @@ color@^4.2:
|
|||||||
color-convert "^2.0.1"
|
color-convert "^2.0.1"
|
||||||
color-string "^1.9.0"
|
color-string "^1.9.0"
|
||||||
|
|
||||||
|
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@^5.0.0:
|
commander@^5.0.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||||
@@ -1553,6 +1574,11 @@ cosmiconfig@^7.0.1:
|
|||||||
path-type "^4.0.0"
|
path-type "^4.0.0"
|
||||||
yaml "^1.10.0"
|
yaml "^1.10.0"
|
||||||
|
|
||||||
|
country-flag-icons@^1.5.4:
|
||||||
|
version "1.5.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/country-flag-icons/-/country-flag-icons-1.5.5.tgz#04a41c83e2ea38ea28054d4e3eff9d1ce16aec1c"
|
||||||
|
integrity sha512-k4WXZ/WvWOSiYXRG1n8EYHNr1m/IX0GffKqAidaet5DrJsDOmJ8Q/8JvvONhZNnKYg24s4lvsm+9og1HcuIU/g==
|
||||||
|
|
||||||
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
cross-spawn@^7.0.2, cross-spawn@^7.0.3:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
|
||||||
@@ -1672,6 +1698,11 @@ defined@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf"
|
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf"
|
||||||
integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==
|
integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==
|
||||||
|
|
||||||
|
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==
|
||||||
|
|
||||||
depd@2.0.0:
|
depd@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
|
||||||
@@ -1745,9 +1776,9 @@ ee-first@1.1.1:
|
|||||||
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
|
||||||
|
|
||||||
electron-to-chromium@^1.4.284:
|
electron-to-chromium@^1.4.284:
|
||||||
version "1.4.312"
|
version "1.4.314"
|
||||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.312.tgz#e70a5b46252814ffc576b2c29032e1a559b9ad53"
|
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.314.tgz#33e4ad7a2ca2ddbe2e113874cc0c0e2a00cb46bf"
|
||||||
integrity sha512-e7g+PzxzkbiCD1aNhdj+Tx3TLlfrQF/Lf+LAaUdoLvB1kCxf9wJimqXdWEqnoiYjFtxIR1hGBmoHsBIcCBNOMA==
|
integrity sha512-+3RmNVx9hZLlc0gW//4yep0K5SYKmIvB5DXg1Yg6varsuAHlHwTeqeygfS8DWwLCsNOWrgj+p9qgM5WYjw1lXQ==
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
@@ -1919,12 +1950,12 @@ escodegen@^1.13.0:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
source-map "~0.6.1"
|
source-map "~0.6.1"
|
||||||
|
|
||||||
eslint-config-next@13.1.5:
|
eslint-config-next@^13.2.2:
|
||||||
version "13.1.5"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.1.5.tgz#38daeaa1463197ef5b6cec9136ce6838ea776cf3"
|
resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.2.2.tgz#eee64ac5160b0741c11dbb6c848882b5f8da03c0"
|
||||||
integrity sha512-7FqkjkvGCQfvYUiPTFRiRYPR1uI6Ew+4f4mVp16lLSWcaChtWoZxQCZHM5n0yxzKKVmuEg1aM4uvDQfSXSjTww==
|
integrity sha512-aI56IaeSHaLevpmD/5sVTRGDp7rs1vFkrJxFsrwCSkd4GbWHkUbam0cv8neoW58fw+O/B05MoK/Nb0rcf9wOZQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@next/eslint-plugin-next" "13.1.5"
|
"@next/eslint-plugin-next" "13.2.2"
|
||||||
"@rushstack/eslint-patch" "^1.1.3"
|
"@rushstack/eslint-patch" "^1.1.3"
|
||||||
"@typescript-eslint/parser" "^5.42.0"
|
"@typescript-eslint/parser" "^5.42.0"
|
||||||
eslint-import-resolver-node "^0.3.6"
|
eslint-import-resolver-node "^0.3.6"
|
||||||
@@ -2410,7 +2441,7 @@ flatted@^3.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
|
||||||
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
|
||||||
|
|
||||||
follow-redirects@^1.14.0, follow-redirects@^1.14.8:
|
follow-redirects@^1.14.0, follow-redirects@^1.14.8, follow-redirects@^1.15.0:
|
||||||
version "1.15.2"
|
version "1.15.2"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
|
||||||
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
|
||||||
@@ -2422,6 +2453,15 @@ for-each@^0.3.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-callable "^1.1.3"
|
is-callable "^1.1.3"
|
||||||
|
|
||||||
|
form-data@^4.0.0:
|
||||||
|
version "4.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
|
||||||
|
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
|
||||||
|
dependencies:
|
||||||
|
asynckit "^0.4.0"
|
||||||
|
combined-stream "^1.0.8"
|
||||||
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
forwarded@0.2.0:
|
forwarded@0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
|
||||||
@@ -2778,6 +2818,11 @@ http-proxy-agent@^5.0.0:
|
|||||||
agent-base "6"
|
agent-base "6"
|
||||||
debug "4"
|
debug "4"
|
||||||
|
|
||||||
|
http-status-codes@^2.2.0:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-2.2.0.tgz#bb2efe63d941dfc2be18e15f703da525169622be"
|
||||||
|
integrity sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==
|
||||||
|
|
||||||
https-proxy-agent@^5.0.0:
|
https-proxy-agent@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
|
||||||
@@ -2851,6 +2896,13 @@ inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.4:
|
|||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
|
|
||||||
|
input-format@^0.3.8:
|
||||||
|
version "0.3.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/input-format/-/input-format-0.3.8.tgz#9445b0cab2f0457fbe36d77d607e942fd37345c5"
|
||||||
|
integrity sha512-tLR0XRig1xIcG1PtIpMd/uoltvkAI62CN9OIbtj4/tEJAkqTCQLNHUZ9N4M46w0dopny7Rlt/lRH5Xzp7e6F+g==
|
||||||
|
dependencies:
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
internal-slot@^1.0.3, internal-slot@^1.0.4:
|
internal-slot@^1.0.3, internal-slot@^1.0.4:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
|
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
|
||||||
@@ -3252,6 +3304,11 @@ levn@~0.3.0:
|
|||||||
prelude-ls "~1.1.2"
|
prelude-ls "~1.1.2"
|
||||||
type-check "~0.3.2"
|
type-check "~0.3.2"
|
||||||
|
|
||||||
|
libphonenumber-js@^1.10.20:
|
||||||
|
version "1.10.21"
|
||||||
|
resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.21.tgz#860312cbec1389e36e28389161025c7817a3ae32"
|
||||||
|
integrity sha512-/udZhx49av2r2gZR/+xXSrwcR8smX/sDNrVpOFrvW+CA26TfYTVZfwb3MIDvmwAYMLs7pXuJjZX0VxxGpqPhsA==
|
||||||
|
|
||||||
lie@3.1.1:
|
lie@3.1.1:
|
||||||
version "3.1.1"
|
version "3.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
resolved "https://registry.yarnpkg.com/lie/-/lie-3.1.1.tgz#9a436b2cc7746ca59de7a41fa469b3efb76bd87e"
|
||||||
@@ -3434,7 +3491,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
|
|||||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||||
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
|
||||||
|
|
||||||
mime-types@^2.0.8, mime-types@~2.1.24, mime-types@~2.1.34:
|
mime-types@^2.0.8, mime-types@^2.1.12, mime-types@~2.1.24, mime-types@~2.1.34:
|
||||||
version "2.1.35"
|
version "2.1.35"
|
||||||
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
|
||||||
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
|
||||||
@@ -3518,30 +3575,30 @@ next-seo@^5.15.0:
|
|||||||
resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-5.15.0.tgz#b1a90508599774982909ea44803323c6fb7b50f4"
|
resolved "https://registry.yarnpkg.com/next-seo/-/next-seo-5.15.0.tgz#b1a90508599774982909ea44803323c6fb7b50f4"
|
||||||
integrity sha512-LGbcY91yDKGMb7YI+28n3g+RuChUkt6pXNpa8FkfKkEmNiJkeRDEXTnnjVtwT9FmMhG6NH8qwHTelGrlYm9rgg==
|
integrity sha512-LGbcY91yDKGMb7YI+28n3g+RuChUkt6pXNpa8FkfKkEmNiJkeRDEXTnnjVtwT9FmMhG6NH8qwHTelGrlYm9rgg==
|
||||||
|
|
||||||
next@^13.2.1:
|
next@^13.2.2:
|
||||||
version "13.2.1"
|
version "13.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/next/-/next-13.2.1.tgz#34d823f518632b36379863228ed9f861c335b9c0"
|
resolved "https://registry.yarnpkg.com/next/-/next-13.2.2.tgz#ee6e6ab1119fb685c598be7305725f2b5833d0e0"
|
||||||
integrity sha512-qhgJlDtG0xidNViJUPeQHLGJJoT4zDj/El7fP3D3OzpxJDUfxsm16cK4WTMyvSX1ciIfAq05u+0HqFAa+VJ+Hg==
|
integrity sha512-dDKfGBqSxqmqx5WN9tDFg0uGUkD/LGUxR29tpe8AEmo2SwfbPWf04qyvDcKmpjt2fCzP4132BvFRZFlg+11kGw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@next/env" "13.2.1"
|
"@next/env" "13.2.2"
|
||||||
"@swc/helpers" "0.4.14"
|
"@swc/helpers" "0.4.14"
|
||||||
caniuse-lite "^1.0.30001406"
|
caniuse-lite "^1.0.30001406"
|
||||||
postcss "8.4.14"
|
postcss "8.4.14"
|
||||||
styled-jsx "5.1.1"
|
styled-jsx "5.1.1"
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
"@next/swc-android-arm-eabi" "13.2.1"
|
"@next/swc-android-arm-eabi" "13.2.2"
|
||||||
"@next/swc-android-arm64" "13.2.1"
|
"@next/swc-android-arm64" "13.2.2"
|
||||||
"@next/swc-darwin-arm64" "13.2.1"
|
"@next/swc-darwin-arm64" "13.2.2"
|
||||||
"@next/swc-darwin-x64" "13.2.1"
|
"@next/swc-darwin-x64" "13.2.2"
|
||||||
"@next/swc-freebsd-x64" "13.2.1"
|
"@next/swc-freebsd-x64" "13.2.2"
|
||||||
"@next/swc-linux-arm-gnueabihf" "13.2.1"
|
"@next/swc-linux-arm-gnueabihf" "13.2.2"
|
||||||
"@next/swc-linux-arm64-gnu" "13.2.1"
|
"@next/swc-linux-arm64-gnu" "13.2.2"
|
||||||
"@next/swc-linux-arm64-musl" "13.2.1"
|
"@next/swc-linux-arm64-musl" "13.2.2"
|
||||||
"@next/swc-linux-x64-gnu" "13.2.1"
|
"@next/swc-linux-x64-gnu" "13.2.2"
|
||||||
"@next/swc-linux-x64-musl" "13.2.1"
|
"@next/swc-linux-x64-musl" "13.2.2"
|
||||||
"@next/swc-win32-arm64-msvc" "13.2.1"
|
"@next/swc-win32-arm64-msvc" "13.2.2"
|
||||||
"@next/swc-win32-ia32-msvc" "13.2.1"
|
"@next/swc-win32-ia32-msvc" "13.2.2"
|
||||||
"@next/swc-win32-x64-msvc" "13.2.1"
|
"@next/swc-win32-x64-msvc" "13.2.2"
|
||||||
|
|
||||||
node-fetch@2.6.7:
|
node-fetch@2.6.7:
|
||||||
version "2.6.7"
|
version "2.6.7"
|
||||||
@@ -3986,11 +4043,11 @@ prettier@^2.8.3:
|
|||||||
integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==
|
integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==
|
||||||
|
|
||||||
prisma@^4.9.0:
|
prisma@^4.9.0:
|
||||||
version "4.10.1"
|
version "4.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.10.1.tgz#88084695d7b364ae6bebf93d5006f84439c4e7d1"
|
resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.11.0.tgz#9695ba4129a43eab3e76b5f7a033c6c020377725"
|
||||||
integrity sha512-0jDxgg+DruB1kHVNlcspXQB9au62IFfVg9drkhzXudszHNUAQn0lVuu+T8np0uC2z1nKD5S3qPeCyR8u5YFLnA==
|
integrity sha512-4zZmBXssPUEiX+GeL0MUq/Yyie4ltiKmGu7jCJFnYMamNrrulTBc+D+QwAQSJ01tyzeGHlD13kOnqPwRipnlNw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@prisma/engines" "4.10.1"
|
"@prisma/engines" "4.11.0"
|
||||||
|
|
||||||
process-warning@^1.0.0:
|
process-warning@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
@@ -4084,6 +4141,11 @@ proxy-addr@~2.0.7:
|
|||||||
forwarded "0.2.0"
|
forwarded "0.2.0"
|
||||||
ipaddr.js "1.9.1"
|
ipaddr.js "1.9.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==
|
||||||
|
|
||||||
pseudomap@^1.0.1:
|
pseudomap@^1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||||
@@ -4165,7 +4227,7 @@ react-device-detect@^2.2.3:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ua-parser-js "^1.0.33"
|
ua-parser-js "^1.0.33"
|
||||||
|
|
||||||
react-dom@18.2.0:
|
react-dom@^18.2.0:
|
||||||
version "18.2.0"
|
version "18.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||||
@@ -4194,6 +4256,11 @@ react-firebase-hooks@^3.0.3:
|
|||||||
resolved "https://registry.yarnpkg.com/react-firebase-hooks/-/react-firebase-hooks-3.0.5.tgz#4b0673ef4c028886a35c4a5c8def1a786d5e33e6"
|
resolved "https://registry.yarnpkg.com/react-firebase-hooks/-/react-firebase-hooks-3.0.5.tgz#4b0673ef4c028886a35c4a5c8def1a786d5e33e6"
|
||||||
integrity sha512-9LztZGaZU0qJY/TQWzkBlfVrFmu0Ui6HE8CUjU7tNz9oGKGx5tUQG/xQbfyMIGClErgnnQsm8sC+oU9pg8Ri5Q==
|
integrity sha512-9LztZGaZU0qJY/TQWzkBlfVrFmu0Ui6HE8CUjU7tNz9oGKGx5tUQG/xQbfyMIGClErgnnQsm8sC+oU9pg8Ri5Q==
|
||||||
|
|
||||||
|
react-hook-form@^7.43.2:
|
||||||
|
version "7.43.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.43.3.tgz#780af64ea1f3c5864626a377e302bfcc7750af6f"
|
||||||
|
integrity sha512-LV6Fixh+hirrl6dXbM78aB6n//82aKbsNbcofF3wc6nx1UJLu3Jj/gsg1E5C9iISnLX+du8VTUyBUz2aCy+H7w==
|
||||||
|
|
||||||
react-hot-toast@^2.4.0:
|
react-hot-toast@^2.4.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.0.tgz#b91e7a4c1b6e3068fc599d3d83b4fb48668ae51d"
|
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.0.tgz#b91e7a4c1b6e3068fc599d3d83b4fb48668ae51d"
|
||||||
@@ -4211,7 +4278,18 @@ react-is@^16.13.1:
|
|||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||||
|
|
||||||
react@18.2.0:
|
react-phone-number-input@^3.2.19:
|
||||||
|
version "3.2.19"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-phone-number-input/-/react-phone-number-input-3.2.19.tgz#78e09f12235b5c3a46555324198abd2c36b69d3e"
|
||||||
|
integrity sha512-3peWqyFgJ3+IvkB0GHvhAzQ6LEfJw1/obG8KPvCcac8OKp9Ir+pLdyySgxbD56EYkaAwZbBwOQJaGNqIX0yrtg==
|
||||||
|
dependencies:
|
||||||
|
classnames "^2.3.1"
|
||||||
|
country-flag-icons "^1.5.4"
|
||||||
|
input-format "^0.3.8"
|
||||||
|
libphonenumber-js "^1.10.20"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
|
react@^18.2.0:
|
||||||
version "18.2.0"
|
version "18.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||||
|
|||||||
Reference in New Issue
Block a user