diff --git a/app/create/VoteForm.tsx b/app/create/VoteForm.tsx
index 231493c..5d51789 100644
--- a/app/create/VoteForm.tsx
+++ b/app/create/VoteForm.tsx
@@ -31,6 +31,9 @@ import { createVote } from "@/lib/actions/vote";
import { Textarea } from "../../components/ui/textarea";
import { IVoteOptions } from "@/lib/types";
+import { useAvailablePhoneNumbers } from "@/lib/hook";
+import PhoneNumberDropdown from "@/components/phone/phone-number-dropdown";
+
const FormSchema = z
.object({
vote_options: z
@@ -43,6 +46,7 @@ const FormSchema = z
.min(5, { message: "Title has a minimum characters of 5" }),
description: z.string().optional(),
end_date: z.date(),
+ phone_number: z.string(),
})
.refine(
(data) => {
@@ -62,6 +66,7 @@ export default function VoteForm() {
defaultValues: {
title: "",
vote_options: [],
+ phone_number: "",
},
});
@@ -105,6 +110,8 @@ export default function VoteForm() {
});
}
+ const { data: availablePhoneNumbers } = useAvailablePhoneNumbers();
+
return (
+ );
+}
diff --git a/lib/actions/vote.ts b/lib/actions/vote.ts
index 41abd5d..31221da 100644
--- a/lib/actions/vote.ts
+++ b/lib/actions/vote.ts
@@ -30,6 +30,7 @@ export async function createVote(data: {
end_date: Date;
title: string;
description?: string;
+ phone_number?: string;
}) {
const supabase = await createSupabaseServer();
@@ -38,6 +39,7 @@ export async function createVote(data: {
title: data.title,
end_date: new Date(data.end_date).toISOString(),
description: data.description || "",
+ phone_number: data.phone_number || "",
});
if (error) {
@@ -61,6 +63,7 @@ export async function updateVoteById(
end_date: Date;
description?: string;
title: string;
+ phone_number: string;
},
voteId: string,
) {
@@ -71,11 +74,13 @@ export async function updateVoteById(
title: data.title,
end_date: data.end_date.toISOString(),
description: data.description,
+ phone_number: data.phone_number,
})
.eq("id", voteId);
if (error) {
throw error.message;
}
+
revalidatePath("/vote/" + voteId);
return redirect("/vote/" + voteId);
}
diff --git a/lib/hook/index.ts b/lib/hook/index.ts
index 0d7a849..133c352 100644
--- a/lib/hook/index.ts
+++ b/lib/hook/index.ts
@@ -1,6 +1,10 @@
import { useQuery, UseQueryResult } from "@tanstack/react-query";
import { createSupabaseBrowser } from "../supabase/client";
-import { sortVoteOptionsBy } from "../utils";
+import {
+ sortVoteOptionsBy,
+ toDisplayedPhoneNumberFormat,
+ toStoredPhoneNumberFormat,
+} from "../utils";
import { IComment } from "../types";
export function useGetVote(id: string) {
@@ -57,3 +61,46 @@ export function useComment(voteId: string) {
staleTime: Infinity,
});
}
+
+const configuredPhoneNumbers = process.env.NEXT_PUBLIC_PHONE_NUMBERS
+ ? process.env.NEXT_PUBLIC_PHONE_NUMBERS.split(",")
+ : [];
+
+export type FormattedNumber = {
+ e164: string;
+ displayNumber: string;
+};
+
+export function useAvailablePhoneNumbers(): UseQueryResult<
+ FormattedNumber[]
+> {
+ const supabase = createSupabaseBrowser();
+
+ return useQuery({
+ queryKey: ["available-phone-numbers"],
+ queryFn: async () => {
+ // Get phone numbers used in all active votes
+ const { error, data } = await supabase
+ .from("vote")
+ .select("phone_number")
+ .filter("end_date", "gte", new Date().toISOString());
+
+ if (error) {
+ console.error(error);
+ throw new Error("Failed to fetch phone numbers");
+ }
+ const usedPhoneNumbers = data.map((row) => row.phone_number);
+
+ const availableNumbers = configuredPhoneNumbers
+ .filter((tel) => !usedPhoneNumbers.includes(tel))
+ .map((tel) => {
+ return {
+ e164: toStoredPhoneNumberFormat(tel),
+ displayNumber: toDisplayedPhoneNumberFormat(tel),
+ };
+ });
+
+ return availableNumbers;
+ },
+ });
+}
diff --git a/lib/types/index.ts b/lib/types/index.ts
index 0fbf7d7..9557423 100644
--- a/lib/types/index.ts
+++ b/lib/types/index.ts
@@ -13,6 +13,7 @@ export type IVote = {
end_date: string;
id: string;
title: string;
+ phone_number: string | null;
};
export type IComment = {
diff --git a/lib/types/supabase.ts b/lib/types/supabase.ts
index e3fc7cd..91bd5fa 100644
--- a/lib/types/supabase.ts
+++ b/lib/types/supabase.ts
@@ -4,264 +4,264 @@ export type Json =
| boolean
| null
| { [key: string]: Json | undefined }
- | Json[];
+ | Json[]
export type Database = {
public: {
Tables: {
comments: {
Row: {
- created_at: string;
- id: string;
- is_edit: boolean;
- send_by: string;
- text: string;
- vote_id: string;
- };
+ created_at: string
+ id: string
+ is_edit: boolean
+ send_by: string
+ text: string
+ vote_id: string
+ }
Insert: {
- created_at?: string;
- id?: string;
- is_edit?: boolean;
- send_by?: string;
- text: string;
- vote_id: string;
- };
+ created_at?: string
+ id?: string
+ is_edit?: boolean
+ send_by?: string
+ text: string
+ vote_id: string
+ }
Update: {
- created_at?: string;
- id?: string;
- is_edit?: boolean;
- send_by?: string;
- text?: string;
- vote_id?: string;
- };
+ created_at?: string
+ id?: string
+ is_edit?: boolean
+ send_by?: string
+ text?: string
+ vote_id?: string
+ }
Relationships: [
{
- foreignKeyName: "comments_vote_id_fkey";
- columns: ["vote_id"];
- isOneToOne: false;
- referencedRelation: "vote";
- referencedColumns: ["id"];
+ foreignKeyName: "comments_vote_id_fkey"
+ columns: ["vote_id"]
+ isOneToOne: false
+ referencedRelation: "vote"
+ referencedColumns: ["id"]
},
{
- foreignKeyName: "public_comments_send_by_fkey";
- columns: ["send_by"];
- isOneToOne: false;
- referencedRelation: "users";
- referencedColumns: ["id"];
+ foreignKeyName: "public_comments_send_by_fkey"
+ columns: ["send_by"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
profile: {
Row: {
- avatar_url: string | null;
- created_at: string;
- email: string | null;
- full_name: string | null;
- id: string;
- phone: string | null;
- updated_at: string;
- };
+ avatar_url: string | null
+ created_at: string
+ email: string | null
+ full_name: string | null
+ id: string
+ phone: string | null
+ updated_at: string
+ }
Insert: {
- avatar_url?: string | null;
- created_at?: string;
- email?: string | null;
- full_name?: string | null;
- id: string;
- phone?: string | null;
- updated_at?: string;
- };
+ avatar_url?: string | null
+ created_at?: string
+ email?: string | null
+ full_name?: string | null
+ id: string
+ phone?: string | null
+ updated_at?: string
+ }
Update: {
- avatar_url?: string | null;
- created_at?: string;
- email?: string | null;
- full_name?: string | null;
- id?: string;
- phone?: string | null;
- updated_at?: string;
- };
+ avatar_url?: string | null
+ created_at?: string
+ email?: string | null
+ full_name?: string | null
+ id?: string
+ phone?: string | null
+ updated_at?: string
+ }
Relationships: [
{
- foreignKeyName: "profile_id_fkey";
- columns: ["id"];
- isOneToOne: true;
- referencedRelation: "users";
- referencedColumns: ["id"];
+ foreignKeyName: "profile_id_fkey"
+ columns: ["id"]
+ isOneToOne: true
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
vote: {
Row: {
- created_at: string;
- created_by: string;
- description: string | null;
- end_date: string;
- id: string;
- title: string;
- };
+ created_at: string
+ created_by: string
+ description: string | null
+ end_date: string
+ id: string
+ phone_number: string | null
+ title: string
+ }
Insert: {
- created_at?: string;
- created_by: string;
- description?: string | null;
- end_date: string;
- id?: string;
- title: string;
- };
+ created_at?: string
+ created_by: string
+ description?: string | null
+ end_date: string
+ id?: string
+ phone_number?: string | null
+ title: string
+ }
Update: {
- created_at?: string;
- created_by?: string;
- description?: string | null;
- end_date?: string;
- id?: string;
- title?: string;
- };
+ created_at?: string
+ created_by?: string
+ description?: string | null
+ end_date?: string
+ id?: string
+ phone_number?: string | null
+ title?: string
+ }
Relationships: [
{
- foreignKeyName: "public_vote_created_by_fkey";
- columns: ["created_by"];
- isOneToOne: false;
- referencedRelation: "users";
- referencedColumns: ["id"];
+ foreignKeyName: "public_vote_created_by_fkey"
+ columns: ["created_by"]
+ isOneToOne: false
+ referencedRelation: "users"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
vote_log: {
Row: {
- created_at: string;
- id: string;
- option: string;
- user_id: string;
- vote_id: string;
- };
+ created_at: string
+ id: string
+ option: string
+ user_id: string
+ vote_id: string
+ }
Insert: {
- created_at?: string;
- id?: string;
- option: string;
- user_id: string;
- vote_id: string;
- };
+ created_at?: string
+ id?: string
+ option: string
+ user_id: string
+ vote_id: string
+ }
Update: {
- created_at?: string;
- id?: string;
- option?: string;
- user_id?: string;
- vote_id?: string;
- };
+ created_at?: string
+ id?: string
+ option?: string
+ user_id?: string
+ vote_id?: string
+ }
Relationships: [
{
- foreignKeyName: "vote_log_vote_id_fkey";
- columns: ["vote_id"];
- isOneToOne: false;
- referencedRelation: "vote";
- referencedColumns: ["id"];
+ foreignKeyName: "vote_log_vote_id_fkey"
+ columns: ["vote_id"]
+ isOneToOne: false
+ referencedRelation: "vote"
+ referencedColumns: ["id"]
},
- ];
- };
+ ]
+ }
vote_options: {
Row: {
- options: Json;
- vote_id: string;
- };
+ options: Json
+ vote_id: string
+ }
Insert: {
- options: Json;
- vote_id: string;
- };
+ options: Json
+ vote_id: string
+ }
Update: {
- options?: Json;
- vote_id?: string;
- };
+ options?: Json
+ vote_id?: string
+ }
Relationships: [
{
- foreignKeyName: "vote_options_vote_id_fkey";
- columns: ["vote_id"];
- isOneToOne: true;
- referencedRelation: "vote";
- referencedColumns: ["id"];
+ foreignKeyName: "vote_options_vote_id_fkey"
+ columns: ["vote_id"]
+ isOneToOne: true
+ referencedRelation: "vote"
+ referencedColumns: ["id"]
},
- ];
- };
- };
+ ]
+ }
+ }
Views: {
- [_ in never]: never;
- };
+ [_ in never]: never
+ }
Functions: {
create_vote: {
Args: {
- options: Json;
- title: string;
- end_date: string;
- description: string;
- };
- Returns: string;
- };
+ options: Json
+ title: string
+ end_date: string
+ description: string
+ phone_number?: string
+ }
+ Returns: string
+ }
get_vote: {
Args: {
- target_vote: string;
- };
+ target_vote: string
+ }
Returns: {
- vote_columns: unknown;
- vote_options_columns: unknown;
- vote_log_columns: unknown;
- }[];
- };
+ vote_columns: unknown
+ vote_options_columns: unknown
+ vote_log_columns: unknown
+ }[]
+ }
is_expired: {
Args: {
- vote_id: string;
- };
- Returns: boolean;
- };
+ vote_id: string
+ }
+ Returns: boolean
+ }
is_voted: {
Args: {
- target_id: string;
- };
- Returns: boolean;
- };
+ target_id: string
+ }
+ Returns: boolean
+ }
update_vote: {
Args: {
- update_id: string;
- option: string;
- };
- Returns: undefined;
- };
- };
+ update_id: string
+ option: string
+ }
+ Returns: undefined
+ }
+ }
Enums: {
- [_ in never]: never;
- };
+ [_ in never]: never
+ }
CompositeTypes: {
- [_ in never]: never;
- };
- };
-};
+ [_ in never]: never
+ }
+ }
+}
-type PublicSchema = Database[Extract];
+type PublicSchema = Database[Extract]
export type Tables<
PublicTableNameOrOptions extends
| keyof (PublicSchema["Tables"] & PublicSchema["Views"])
| { schema: keyof Database },
TableName extends PublicTableNameOrOptions extends { schema: keyof Database }
- ? keyof (
- & Database[PublicTableNameOrOptions["schema"]]["Tables"]
- & Database[PublicTableNameOrOptions["schema"]]["Views"]
- )
+ ? keyof (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
+ Database[PublicTableNameOrOptions["schema"]]["Views"])
: never = never,
-> = PublicTableNameOrOptions extends { schema: keyof Database } ? (
- & Database[PublicTableNameOrOptions["schema"]]["Tables"]
- & Database[PublicTableNameOrOptions["schema"]]["Views"]
- )[TableName] extends {
- Row: infer R;
- } ? R
- : never
- : PublicTableNameOrOptions extends keyof (
- & PublicSchema["Tables"]
- & PublicSchema["Views"]
- ) ? (
- & PublicSchema["Tables"]
- & PublicSchema["Views"]
- )[PublicTableNameOrOptions] extends {
- Row: infer R;
- } ? R
+> = PublicTableNameOrOptions extends { schema: keyof Database }
+ ? (Database[PublicTableNameOrOptions["schema"]]["Tables"] &
+ Database[PublicTableNameOrOptions["schema"]]["Views"])[TableName] extends {
+ Row: infer R
+ }
+ ? R
+ : never
+ : PublicTableNameOrOptions extends keyof (PublicSchema["Tables"] &
+ PublicSchema["Views"])
+ ? (PublicSchema["Tables"] &
+ PublicSchema["Views"])[PublicTableNameOrOptions] extends {
+ Row: infer R
+ }
+ ? R
+ : never
: never
- : never;
export type TablesInsert<
PublicTableNameOrOptions extends
@@ -272,15 +272,17 @@ export type TablesInsert<
: never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
- Insert: infer I;
- } ? I
- : never
+ Insert: infer I
+ }
+ ? I
+ : never
: PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
- Insert: infer I;
- } ? I
+ Insert: infer I
+ }
+ ? I
+ : never
: never
- : never;
export type TablesUpdate<
PublicTableNameOrOptions extends
@@ -291,15 +293,17 @@ export type TablesUpdate<
: never = never,
> = PublicTableNameOrOptions extends { schema: keyof Database }
? Database[PublicTableNameOrOptions["schema"]]["Tables"][TableName] extends {
- Update: infer U;
- } ? U
- : never
+ Update: infer U
+ }
+ ? U
+ : never
: PublicTableNameOrOptions extends keyof PublicSchema["Tables"]
? PublicSchema["Tables"][PublicTableNameOrOptions] extends {
- Update: infer U;
- } ? U
+ Update: infer U
+ }
+ ? U
+ : never
: never
- : never;
export type Enums<
PublicEnumNameOrOptions extends
@@ -312,4 +316,4 @@ export type Enums<
? Database[PublicEnumNameOrOptions["schema"]]["Enums"][EnumName]
: PublicEnumNameOrOptions extends keyof PublicSchema["Enums"]
? PublicSchema["Enums"][PublicEnumNameOrOptions]
- : never;
+ : never
diff --git a/lib/utils.ts b/lib/utils.ts
index f523eed..1051ec6 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -1,10 +1,24 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { IVoteOptions } from "./types";
+import { parsePhoneNumber } from "libphonenumber-js";
+
+export { parsePhoneNumber } from "libphonenumber-js";
+
+export function toStoredPhoneNumberFormat(phoneNumber: string) {
+ const parseNumber = parsePhoneNumber(phoneNumber);
+ return parseNumber.format("E.164");
+}
+
+export function toDisplayedPhoneNumberFormat(phoneNumber: string) {
+ const parseNumber = parsePhoneNumber(phoneNumber);
+ return parseNumber.formatInternational();
+}
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
+
export function nextWeek() {
let currentDate = new Date();
let nextWeekDate = new Date();
diff --git a/package-lock.json b/package-lock.json
index f16140c..453c468 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,6 +26,8 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
+ "fast-jwt": "^4.0.1",
+ "libphonenumber-js": "^1.11.3",
"lucide-react": "^0.294.0",
"next": "^14.2.3",
"next-themes": "^0.2.1",
@@ -38,6 +40,7 @@
"sharp": "^0.33.0",
"tailwind-merge": "^2.1.0",
"tailwindcss-animate": "^1.0.7",
+ "twilio": "^5.1.1",
"uuid": "^9.0.1",
"zod": "^3.22.4"
},
@@ -803,6 +806,14 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
+ "node_modules/@lukeed/ms": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz",
+ "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/@next/env": {
"version": "14.2.3",
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.3.tgz",
@@ -2279,6 +2290,17 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "dependencies": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
"node_modules/ast-types-flow": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -2297,8 +2319,7 @@
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
- "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
- "dev": true
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/autoprefixer": {
"version": "10.4.16",
@@ -2358,6 +2379,16 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz",
+ "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==",
+ "dependencies": {
+ "follow-redirects": "^1.15.6",
+ "form-data": "^4.0.0",
+ "proxy-from-env": "^1.1.0"
+ }
+ },
"node_modules/axobject-query": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz",
@@ -2395,6 +2426,11 @@
"node": ">=8"
}
},
+ "node_modules/bn.js": {
+ "version": "4.12.0",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
+ "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
+ },
"node_modules/brace-expansion": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -2447,6 +2483,11 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
+ },
"node_modules/busboy": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
@@ -2462,7 +2503,6 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz",
"integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==",
- "dev": true,
"dependencies": {
"function-bind": "^1.1.2",
"get-intrinsic": "^1.2.1",
@@ -2644,7 +2684,6 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
- "dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
@@ -2733,11 +2772,15 @@
"url": "https://opencollective.com/date-fns"
}
},
+ "node_modules/dayjs": {
+ "version": "1.11.11",
+ "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz",
+ "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg=="
+ },
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dev": true,
"dependencies": {
"ms": "2.1.2"
},
@@ -2760,7 +2803,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz",
"integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==",
- "dev": true,
"dependencies": {
"get-intrinsic": "^1.2.1",
"gopd": "^1.0.1",
@@ -2791,7 +2833,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "dev": true,
"engines": {
"node": ">=0.4.0"
}
@@ -2858,6 +2899,14 @@
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"dev": true
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/electron-to-chromium": {
"version": "1.4.601",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.601.tgz",
@@ -3467,6 +3516,20 @@
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
"dev": true
},
+ "node_modules/fast-jwt": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/fast-jwt/-/fast-jwt-4.0.1.tgz",
+ "integrity": "sha512-+mdSoH0QdOdFSbbGBctJu7L1yfXRtbmjbVJ4W/PEjyvivobDena0RKwihtBkOML1P+kUJ1QuewnH8u+mROsR1w==",
+ "dependencies": {
+ "@lukeed/ms": "^2.0.1",
+ "asn1.js": "^5.4.1",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "mnemonist": "^0.39.5"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
@@ -3563,6 +3626,25 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true
},
+ "node_modules/follow-redirects": {
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
"node_modules/for-each": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz",
@@ -3592,7 +3674,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
- "dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -3684,7 +3765,6 @@
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz",
"integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==",
- "dev": true,
"dependencies": {
"function-bind": "^1.1.2",
"has-proto": "^1.0.1",
@@ -3824,7 +3904,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
- "dev": true,
"dependencies": {
"get-intrinsic": "^1.1.3"
},
@@ -3865,7 +3944,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz",
"integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==",
- "dev": true,
"dependencies": {
"get-intrinsic": "^1.2.2"
},
@@ -3877,7 +3955,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
- "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -3889,7 +3966,6 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
- "dev": true,
"engines": {
"node": ">= 0.4"
},
@@ -4445,6 +4521,27 @@
"json5": "lib/cli.js"
}
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -4460,6 +4557,25 @@
"node": ">=4.0"
}
},
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/keyv": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -4500,6 +4616,11 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/libphonenumber-js": {
+ "version": "1.11.3",
+ "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.3.tgz",
+ "integrity": "sha512-RU0CTsLCu2v6VEzdP+W6UU2n5+jEpMDRkGxUeBgsAJgre3vKgm17eApISH9OQY4G0jZYJVIc8qXmz6CJFueAFg=="
+ },
"node_modules/lilconfig": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
@@ -4528,12 +4649,47 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -4588,7 +4744,6 @@
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
- "dev": true,
"engines": {
"node": ">= 0.6"
}
@@ -4597,7 +4752,6 @@
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
- "dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
@@ -4605,6 +4759,11 @@
"node": ">= 0.6"
}
},
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -4735,11 +4894,18 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/mnemonist": {
+ "version": "0.39.8",
+ "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz",
+ "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==",
+ "dependencies": {
+ "obliterator": "^2.0.1"
+ }
+ },
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/mz": {
"version": "2.7.0",
@@ -4949,7 +5115,6 @@
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
- "dev": true,
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
@@ -5054,6 +5219,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/obliterator": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz",
+ "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ=="
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -5376,6 +5546,11 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/proxy-from-env": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -5389,7 +5564,6 @@
"version": "6.11.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz",
"integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==",
- "dev": true,
"dependencies": {
"side-channel": "^1.0.4"
},
@@ -5743,6 +5917,25 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/safe-regex-test": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
@@ -5757,6 +5950,11 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
@@ -5765,6 +5963,11 @@
"loose-envify": "^1.1.0"
}
},
+ "node_modules/scmp": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz",
+ "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q=="
+ },
"node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
@@ -5783,7 +5986,6 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz",
"integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==",
- "dev": true,
"dependencies": {
"define-data-property": "^1.1.1",
"get-intrinsic": "^1.2.1",
@@ -5872,7 +6074,6 @@
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
- "dev": true,
"dependencies": {
"call-bind": "^1.0.0",
"get-intrinsic": "^1.0.2",
@@ -6373,6 +6574,46 @@
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
+ "node_modules/twilio": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/twilio/-/twilio-5.1.1.tgz",
+ "integrity": "sha512-YpOvpQM17UW72QxK5ukMN0RCY0DdEzI+hTTXxHHhlOtuvpP50JMY0NtkvUViWzZVPJSegJrZPjX43GqmhL/7aw==",
+ "dependencies": {
+ "axios": "^1.6.8",
+ "dayjs": "^1.11.9",
+ "https-proxy-agent": "^5.0.0",
+ "jsonwebtoken": "^9.0.2",
+ "qs": "^6.9.4",
+ "scmp": "^2.1.0",
+ "xmlbuilder": "^13.0.2"
+ },
+ "engines": {
+ "node": ">=14.0"
+ }
+ },
+ "node_modules/twilio/node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/twilio/node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -6844,6 +7085,14 @@
}
}
},
+ "node_modules/xmlbuilder": {
+ "version": "13.0.2",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz",
+ "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
diff --git a/package.json b/package.json
index 2f8aae8..a1c0cb8 100644
--- a/package.json
+++ b/package.json
@@ -28,6 +28,8 @@
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
"date-fns": "^2.30.0",
+ "fast-jwt": "^4.0.1",
+ "libphonenumber-js": "^1.11.3",
"lucide-react": "^0.294.0",
"next": "^14.2.3",
"next-themes": "^0.2.1",
@@ -40,6 +42,7 @@
"sharp": "^0.33.0",
"tailwind-merge": "^2.1.0",
"tailwindcss-animate": "^1.0.7",
+ "twilio": "^5.1.1",
"uuid": "^9.0.1",
"zod": "^3.22.4"
},
diff --git a/supabase/migrations/20240606191502_add_phone_number_voting.sql b/supabase/migrations/20240606191502_add_phone_number_voting.sql
new file mode 100644
index 0000000..c7de7a1
--- /dev/null
+++ b/supabase/migrations/20240606191502_add_phone_number_voting.sql
@@ -0,0 +1,69 @@
+alter table "public"."vote" add column "phone_number" text;
+
+DROP FUNCTION IF EXISTS "public"."create_vote"(options jsonb, title text, end_date timestamp without time zone, description text);
+
+
+CREATE OR REPLACE FUNCTION "public"."create_vote"("options" "jsonb", "title" "text", "end_date" timestamp without time zone, "description" "text", "phone_number" "text" DEFAULT NULL::"text") RETURNS "uuid"
+ LANGUAGE "plpgsql" SECURITY DEFINER
+ SET "search_path" TO 'public'
+ AS $$
+DECLARE
+ return_id uuid;
+ options_count INT;
+ key_value_type text;
+ position_value_type text;
+ vote_count_value_type text;
+BEGIN
+
+
+ SELECT COUNT(*) INTO options_count
+ FROM jsonb_object_keys(options);
+
+
+ IF options_count <= 1 THEN
+ RAISE EXCEPTION 'Options must have more than one key.';
+ END IF;
+
+
+ -- Check if all values associated with keys are objects
+ SELECT jsonb_typeof(value) INTO key_value_type
+ FROM jsonb_each(options)
+ WHERE NOT jsonb_typeof(value) IN ('object');
+
+
+ IF key_value_type IS NOT NULL THEN
+ RAISE EXCEPTION 'All values in options must be objects.';
+ END IF;
+
+
+ -- Check if all positions are numbers
+ SELECT jsonb_typeof(value) INTO position_value_type
+ FROM jsonb_each(options::jsonb -> 'position')
+ WHERE NOT jsonb_typeof(value) IN ('number');
+
+
+ IF position_value_type IS NOT NULL THEN
+ RAISE EXCEPTION 'All positions in options must be numbers.';
+ END IF;
+
+
+ -- Check if all vote_count are numbers
+ SELECT jsonb_typeof(value) INTO vote_count_value_type
+ FROM jsonb_each(options::jsonb -> 'vote_count')
+ WHERE NOT jsonb_typeof(value) IN ('number');
+
+
+ IF vote_count_value_type IS NOT NULL THEN
+ RAISE EXCEPTION 'All vote_count in options must be numbers.';
+ END IF;
+
+
+ INSERT INTO vote (created_by, title, end_date, description, phone_number)
+ VALUES (auth.uid(),title, end_date, description, phone_number)
+ RETURNING id INTO return_id;
+
+
+ INSERT INTO vote_options (vote_id,options)
+ VALUES (return_id, options);
+ return return_id;
+END $$;
\ No newline at end of file