From c6547a108b2f8855a39160bf2b2dbaa64b06ef51 Mon Sep 17 00:00:00 2001 From: jacob-cob-null Date: Thu, 12 Feb 2026 11:13:56 +0800 Subject: [PATCH 1/5] fix(dashboard): Fixed issue in team creation --- actions/teams.ts | 14 +- app/dashboard/components/NewTeamBtn.tsx | 4 +- .../components/team-table/EditTeamModal.tsx | 6 +- .../components/team-table/TeamTable.tsx | 19 +- prisma/schema.prisma | 535 ++++++++++-------- 5 files changed, 317 insertions(+), 261 deletions(-) diff --git a/actions/teams.ts b/actions/teams.ts index 5365d74..be2711c 100644 --- a/actions/teams.ts +++ b/actions/teams.ts @@ -29,7 +29,7 @@ export async function getTeams() { id: true, name: true, _count: { - select: { teamMembers: true }, + select: { team_members: true }, }, }, }); @@ -38,7 +38,7 @@ export async function getTeams() { return teams.map((team) => ({ id: team.id, name: team.name, - memberCount: team._count.teamMembers, + memberCount: team._count.team_members, })); } @@ -74,7 +74,7 @@ export async function createTeam(teamName: string) { // Validate team inputs const result = Team.safeParse({ name: teamName, leader_id: leaderId }); if (!result.success) { - return { success: false, error: result.error.issues[0].message }; + throw new Error(result.error.issues[0].message); } try { @@ -106,7 +106,7 @@ export async function createTeam(teamName: string) { }; } catch (error) { console.error("Database Error:", error); - return { success: false, error: "Failed to create team" }; + throw new Error(error instanceof Error ? error.message : "Failed to create team"); } } @@ -136,7 +136,7 @@ export async function deleteTeam(teamId: string) { return { success: true }; } catch (error) { console.error("Database Error:", error); - return { success: false, error: "Failed to delete team" }; + throw new Error(error instanceof Error ? error.message : "Failed to delete team"); } } // Update teams @@ -150,7 +150,7 @@ export async function updateTeam(teamId: string, teamName: string) { // Validate team inputs const result = Team.safeParse({ name: teamName, leader_id: leaderId }); if (!result.success) { - return { success: false, error: result.error.issues[0].message }; + throw new Error(result.error.issues[0].message); } try { @@ -172,6 +172,6 @@ export async function updateTeam(teamId: string, teamName: string) { return { success: true }; } catch (error) { console.error("Database Error:", error); - return { success: false, error: "Failed to update team" }; + throw new Error(error instanceof Error ? error.message : "Failed to update team"); } } diff --git a/app/dashboard/components/NewTeamBtn.tsx b/app/dashboard/components/NewTeamBtn.tsx index 9250f36..e86fa6d 100644 --- a/app/dashboard/components/NewTeamBtn.tsx +++ b/app/dashboard/components/NewTeamBtn.tsx @@ -25,13 +25,15 @@ function NewTeamBtn() { if (!teamName.trim()) return; setIsLoading(true); + const loadingToast = toast.loading("Creating team..."); try { - toast.loading("Creating team..."); await createTeam(teamName); + toast.dismiss(loadingToast); toast.success(`Team "${teamName}" created successfully!`); setTeamName(""); setOpen(false); } catch (error) { + toast.dismiss(loadingToast); toast.error( error instanceof Error ? error.message : "Failed to create team", ); diff --git a/app/dashboard/components/team-table/EditTeamModal.tsx b/app/dashboard/components/team-table/EditTeamModal.tsx index e023e82..9b2de81 100644 --- a/app/dashboard/components/team-table/EditTeamModal.tsx +++ b/app/dashboard/components/team-table/EditTeamModal.tsx @@ -36,14 +36,14 @@ function EditTeamModal({ team, open, onOpenChange }: EditTeamModalProps) { if (!teamName.trim() || !team) return; setIsLoading(true); + const loadingToast = toast.loading("Updating team..."); try { - toast.loading("Updating team..."); await updateTeam(team.id, teamName); - toast.dismiss(); + toast.dismiss(loadingToast); toast.success(`Team "${teamName}" updated successfully!`); onOpenChange(false); } catch (error) { - toast.dismiss(); + toast.dismiss(loadingToast); toast.error( error instanceof Error ? error.message : "Failed to update team", ); diff --git a/app/dashboard/components/team-table/TeamTable.tsx b/app/dashboard/components/team-table/TeamTable.tsx index df02f6e..be03384 100644 --- a/app/dashboard/components/team-table/TeamTable.tsx +++ b/app/dashboard/components/team-table/TeamTable.tsx @@ -32,12 +32,19 @@ export default function TeamTable({ data = sampleData }: TeamTableProps) { const handleConfirmDelete = async () => { if (teamToDelete) { - toast.loading(`Deleting team ${teamToDelete.name} ...`); - await deleteTeam(teamToDelete.id); - setDeleteModalOpen(false); - setTeamToDelete(null); - toast.dismiss(); - toast.success(`Team ${teamToDelete.name} deleted successfully!`); + const loadingToast = toast.loading(`Deleting team ${teamToDelete.name}...`); + try { + await deleteTeam(teamToDelete.id); + setDeleteModalOpen(false); + setTeamToDelete(null); + toast.dismiss(loadingToast); + toast.success(`Team ${teamToDelete.name} deleted successfully!`); + } catch (error) { + toast.dismiss(loadingToast); + toast.error( + error instanceof Error ? error.message : "Failed to delete team", + ); + } } }; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 82d906b..ab32b7a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -9,250 +9,8 @@ datasource db { schemas = ["auth", "public"] } -/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. -model users { - instance_id String? @db.Uuid - id String @id @db.Uuid - aud String? @db.VarChar(255) - role String? @db.VarChar(255) - email String? @db.VarChar(255) - encrypted_password String? @db.VarChar(255) - email_confirmed_at DateTime? @db.Timestamptz(6) - invited_at DateTime? @db.Timestamptz(6) - confirmation_token String? @db.VarChar(255) - confirmation_sent_at DateTime? @db.Timestamptz(6) - recovery_token String? @db.VarChar(255) - recovery_sent_at DateTime? @db.Timestamptz(6) - email_change_token_new String? @db.VarChar(255) - email_change String? @db.VarChar(255) - email_change_sent_at DateTime? @db.Timestamptz(6) - last_sign_in_at DateTime? @db.Timestamptz(6) - raw_app_meta_data Json? - raw_user_meta_data Json? - is_super_admin Boolean? - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - phone String? @unique - phone_confirmed_at DateTime? @db.Timestamptz(6) - phone_change String? @default("") - phone_change_token String? @default("") @db.VarChar(255) - phone_change_sent_at DateTime? @db.Timestamptz(6) - confirmed_at DateTime? @default(dbgenerated("LEAST(email_confirmed_at, phone_confirmed_at)")) @db.Timestamptz(6) - email_change_token_current String? @default("") @db.VarChar(255) - email_change_confirm_status Int? @default(0) @db.SmallInt - banned_until DateTime? @db.Timestamptz(6) - reauthentication_token String? @default("") @db.VarChar(255) - reauthentication_sent_at DateTime? @db.Timestamptz(6) - is_sso_user Boolean @default(false) - deleted_at DateTime? @db.Timestamptz(6) - is_anonymous Boolean @default(false) - identities identities[] - mfa_factors mfa_factors[] - oauth_authorizations oauth_authorizations[] - oauth_consents oauth_consents[] - one_time_tokens one_time_tokens[] - sessions sessions[] - profile profiles? - - @@index([instance_id]) - @@index([is_anonymous]) - @@schema("auth") -} - -model identities { - provider_id String - user_id String @db.Uuid - identity_data Json - provider String - last_sign_in_at DateTime? @db.Timestamptz(6) - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - email String? @default(dbgenerated("lower((identity_data ->> 'email'::text))")) - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([provider_id, provider], map: "identities_provider_id_provider_unique") - @@index([email]) - @@index([user_id]) - @@schema("auth") -} - -model sessions { - id String @id @db.Uuid - user_id String @db.Uuid - created_at DateTime? @db.Timestamptz(6) - updated_at DateTime? @db.Timestamptz(6) - factor_id String? @db.Uuid - aal aal_level? - not_after DateTime? @db.Timestamptz(6) - refreshed_at DateTime? @db.Timestamp(6) - user_agent String? - ip String? @db.Inet - tag String? - oauth_client_id String? @db.Uuid - refresh_token_hmac_key String? - refresh_token_counter BigInt? - scopes String? - mfa_amr_claims mfa_amr_claims[] - refresh_tokens refresh_tokens[] - oauth_clients oauth_clients? @relation(fields: [oauth_client_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@index([not_after(sort: Desc)]) - @@index([oauth_client_id]) - @@index([user_id]) - @@index([user_id, created_at], map: "user_id_created_at_idx") - @@schema("auth") -} - -model mfa_factors { - id String @id @db.Uuid - user_id String @db.Uuid - friendly_name String? - factor_type factor_type - status factor_status - created_at DateTime @db.Timestamptz(6) - updated_at DateTime @db.Timestamptz(6) - secret String? - phone String? - last_challenged_at DateTime? @unique @db.Timestamptz(6) - web_authn_credential Json? - web_authn_aaguid String? @db.Uuid - last_webauthn_challenge_data Json? - mfa_challenges mfa_challenges[] - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([user_id, phone], map: "unique_phone_factor_per_user") - @@index([user_id, created_at], map: "factor_id_created_at_idx") - @@index([user_id]) - @@schema("auth") -} - -model oauth_authorizations { - id String @id @db.Uuid - authorization_id String @unique - client_id String @db.Uuid - user_id String? @db.Uuid - redirect_uri String - scope String - state String? - resource String? - code_challenge String? - code_challenge_method code_challenge_method? - response_type oauth_response_type @default(code) - status oauth_authorization_status @default(pending) - authorization_code String? @unique - created_at DateTime @default(now()) @db.Timestamptz(6) - expires_at DateTime @default(dbgenerated("(now() + '00:03:00'::interval)")) @db.Timestamptz(6) - approved_at DateTime? @db.Timestamptz(6) - nonce String? - oauth_clients oauth_clients @relation(fields: [client_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - users users? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@schema("auth") -} - -model oauth_consents { - id String @id @db.Uuid - user_id String @db.Uuid - client_id String @db.Uuid - scopes String - granted_at DateTime @default(now()) @db.Timestamptz(6) - revoked_at DateTime? @db.Timestamptz(6) - oauth_clients oauth_clients @relation(fields: [client_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([user_id, client_id], map: "oauth_consents_user_client_unique") - @@index([user_id, granted_at(sort: Desc)], map: "oauth_consents_user_order_idx") - @@schema("auth") -} - -model one_time_tokens { - id String @id @db.Uuid - user_id String @db.Uuid - token_type one_time_token_type - token_hash String - relates_to String - created_at DateTime @default(now()) @db.Timestamp(6) - updated_at DateTime @default(now()) @db.Timestamp(6) - users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([user_id, token_type]) - @@index([relates_to], map: "one_time_tokens_relates_to_hash_idx", type: Hash) - @@index([token_hash], map: "one_time_tokens_token_hash_hash_idx", type: Hash) - @@schema("auth") -} - -model profiles { - id String @id @db.Uuid - email String - full_name String? - avatar_url String? - google_refresh_token String? - created_at DateTime? @default(now()) @db.Timestamptz(6) - user users @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) - teams teams[] - - @@schema("public") -} - -model teams { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - leader_id String @db.Uuid - name String - google_calendar_id String? - created_at DateTime? @default(now()) @db.Timestamptz(6) - announcements announcements[] - events events[] - teamMembers team_members[] - leader profiles @relation(fields: [leader_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@index([leader_id], map: "idx_teams_leader_id") - @@schema("public") -} - -model members { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - email String @unique - full_name String? - created_at DateTime? @default(now()) @db.Timestamptz(6) - teamMembers team_members[] - - @@schema("public") -} - -model team_members { - id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid - team_id String @db.Uuid - member_id String @db.Uuid - added_at DateTime? @default(now()) @db.Timestamptz(6) - created_by String? @db.Uuid - member members @relation(fields: [member_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - team teams @relation(fields: [team_id], references: [id], onDelete: Cascade, onUpdate: NoAction) - - @@unique([team_id, member_id]) - @@index([member_id], map: "idx_team_members_member_id") - @@index([team_id], map: "idx_team_members_team_id") - @@schema("public") -} - -model announcements { - id BigInt @id(map: "announcements_pkey1") @default(autoincrement()) - created_at DateTime @default(now()) @db.Timestamptz(6) - title String - content String - team_id String @default(dbgenerated("gen_random_uuid()")) @db.Uuid - resend_batch_id String? - email_status EmailStatus? @default(PENDING) - sent_at DateTime? @db.Timestamptz(6) - error_message String? - recipient_count Int @default(0) @db.SmallInt - delivered_count Int @default(0) @db.SmallInt - team teams @relation(fields: [team_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "announcements_team_id_fkey1") - - @@schema("public") -} - +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model audit_log_entries { instance_id String? @db.Uuid id String @id @db.Uuid @@ -264,6 +22,8 @@ model audit_log_entries { @@schema("auth") } +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model flow_state { id String @id @db.Uuid user_id String? @db.Uuid @@ -290,6 +50,28 @@ model flow_state { @@schema("auth") } +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model identities { + provider_id String + user_id String @db.Uuid + identity_data Json + provider String + last_sign_in_at DateTime? @db.Timestamptz(6) + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + email String? @default(dbgenerated("lower((identity_data ->> 'email'::text))")) + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([provider_id, provider], map: "identities_provider_id_provider_unique") + @@index([email]) + @@index([user_id]) + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model instances { id String @id @db.Uuid uuid String? @db.Uuid @@ -300,6 +82,8 @@ model instances { @@schema("auth") } +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model mfa_amr_claims { session_id String @db.Uuid created_at DateTime @db.Timestamptz(6) @@ -312,6 +96,8 @@ model mfa_amr_claims { @@schema("auth") } +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model mfa_challenges { id String @id @db.Uuid factor_id String @db.Uuid @@ -326,6 +112,57 @@ model mfa_challenges { @@schema("auth") } +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model mfa_factors { + id String @id @db.Uuid + user_id String @db.Uuid + friendly_name String? + factor_type factor_type + status factor_status + created_at DateTime @db.Timestamptz(6) + updated_at DateTime @db.Timestamptz(6) + secret String? + phone String? + last_challenged_at DateTime? @unique @db.Timestamptz(6) + web_authn_credential Json? + web_authn_aaguid String? @db.Uuid + last_webauthn_challenge_data Json? + mfa_challenges mfa_challenges[] + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([user_id, phone], map: "unique_phone_factor_per_user") + @@index([user_id, created_at], map: "factor_id_created_at_idx") + @@index([user_id]) + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +model oauth_authorizations { + id String @id @db.Uuid + authorization_id String @unique + client_id String @db.Uuid + user_id String? @db.Uuid + redirect_uri String + scope String + state String? + resource String? + code_challenge String? + code_challenge_method code_challenge_method? + response_type oauth_response_type @default(code) + status oauth_authorization_status @default(pending) + authorization_code String? @unique + created_at DateTime @default(now()) @db.Timestamptz(6) + expires_at DateTime @default(dbgenerated("(now() + '00:03:00'::interval)")) @db.Timestamptz(6) + approved_at DateTime? @db.Timestamptz(6) + nonce String? + oauth_clients oauth_clients @relation(fields: [client_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + users users? @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments model oauth_client_states { id String @id @db.Uuid provider_type String @@ -336,6 +173,7 @@ model oauth_client_states { @@schema("auth") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. model oauth_clients { id String @id @db.Uuid client_secret_hash String? @@ -358,6 +196,42 @@ model oauth_clients { @@schema("auth") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +model oauth_consents { + id String @id @db.Uuid + user_id String @db.Uuid + client_id String @db.Uuid + scopes String + granted_at DateTime @default(now()) @db.Timestamptz(6) + revoked_at DateTime? @db.Timestamptz(6) + oauth_clients oauth_clients @relation(fields: [client_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([user_id, client_id], map: "oauth_consents_user_client_unique") + @@index([user_id, granted_at(sort: Desc)], map: "oauth_consents_user_order_idx") + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model one_time_tokens { + id String @id @db.Uuid + user_id String @db.Uuid + token_type one_time_token_type + token_hash String + relates_to String + created_at DateTime @default(now()) @db.Timestamp(6) + updated_at DateTime @default(now()) @db.Timestamp(6) + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([user_id, token_type]) + @@index([relates_to], map: "one_time_tokens_relates_to_hash_idx", type: Hash) + @@index([token_hash], map: "one_time_tokens_token_hash_hash_idx", type: Hash) + @@schema("auth") +} + +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model refresh_tokens { instance_id String? @db.Uuid id BigInt @id @default(autoincrement()) @@ -378,6 +252,9 @@ model refresh_tokens { @@schema("auth") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model saml_providers { id String @id @db.Uuid sso_provider_id String @db.Uuid @@ -394,6 +271,9 @@ model saml_providers { @@schema("auth") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model saml_relay_states { id String @id @db.Uuid sso_provider_id String @db.Uuid @@ -412,12 +292,48 @@ model saml_relay_states { @@schema("auth") } +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model schema_migrations { version String @id @db.VarChar(255) @@schema("auth") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model sessions { + id String @id @db.Uuid + user_id String @db.Uuid + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + factor_id String? @db.Uuid + aal aal_level? + not_after DateTime? @db.Timestamptz(6) + refreshed_at DateTime? @db.Timestamp(6) + user_agent String? + ip String? @db.Inet + tag String? + oauth_client_id String? @db.Uuid + refresh_token_hmac_key String? + refresh_token_counter BigInt? + scopes String? + mfa_amr_claims mfa_amr_claims[] + refresh_tokens refresh_tokens[] + oauth_clients oauth_clients? @relation(fields: [oauth_client_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + users users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([not_after(sort: Desc)]) + @@index([oauth_client_id]) + @@index([user_id]) + @@index([user_id, created_at], map: "user_id_created_at_idx") + @@schema("auth") +} + +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. /// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. model sso_domains { id String @id @db.Uuid @@ -431,6 +347,9 @@ model sso_domains { @@schema("auth") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. /// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. model sso_providers { id String @id @db.Uuid @@ -446,6 +365,77 @@ model sso_providers { @@schema("auth") } +/// This table contains check constraints and requires additional setup for migrations. Visit https://pris.ly/d/check-constraints for more info. +/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +/// This model contains an expression index which requires additional setup for migrations. Visit https://pris.ly/d/expression-indexes for more info. +model users { + instance_id String? @db.Uuid + id String @id @db.Uuid + aud String? @db.VarChar(255) + role String? @db.VarChar(255) + email String? @db.VarChar(255) + encrypted_password String? @db.VarChar(255) + email_confirmed_at DateTime? @db.Timestamptz(6) + invited_at DateTime? @db.Timestamptz(6) + confirmation_token String? @db.VarChar(255) + confirmation_sent_at DateTime? @db.Timestamptz(6) + recovery_token String? @db.VarChar(255) + recovery_sent_at DateTime? @db.Timestamptz(6) + email_change_token_new String? @db.VarChar(255) + email_change String? @db.VarChar(255) + email_change_sent_at DateTime? @db.Timestamptz(6) + last_sign_in_at DateTime? @db.Timestamptz(6) + raw_app_meta_data Json? + raw_user_meta_data Json? + is_super_admin Boolean? + created_at DateTime? @db.Timestamptz(6) + updated_at DateTime? @db.Timestamptz(6) + phone String? @unique + phone_confirmed_at DateTime? @db.Timestamptz(6) + phone_change String? @default("") + phone_change_token String? @default("") @db.VarChar(255) + phone_change_sent_at DateTime? @db.Timestamptz(6) + confirmed_at DateTime? @default(dbgenerated("LEAST(email_confirmed_at, phone_confirmed_at)")) @db.Timestamptz(6) + email_change_token_current String? @default("") @db.VarChar(255) + email_change_confirm_status Int? @default(0) @db.SmallInt + banned_until DateTime? @db.Timestamptz(6) + reauthentication_token String? @default("") @db.VarChar(255) + reauthentication_sent_at DateTime? @db.Timestamptz(6) + is_sso_user Boolean @default(false) + deleted_at DateTime? @db.Timestamptz(6) + is_anonymous Boolean @default(false) + identities identities[] + mfa_factors mfa_factors[] + oauth_authorizations oauth_authorizations[] + oauth_consents oauth_consents[] + one_time_tokens one_time_tokens[] + sessions sessions[] + profiles profiles? + + @@index([instance_id]) + @@index([is_anonymous]) + @@schema("auth") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model announcements { + id BigInt @id(map: "announcements_pkey1") @default(autoincrement()) + created_at DateTime @default(now()) @db.Timestamptz(6) + title String + content String + team_id String @default(dbgenerated("gen_random_uuid()")) @db.Uuid + resend_batch_id String? + email_status EmailStatus? @default(PENDING) + sent_at DateTime? @db.Timestamptz(6) + error_message String? + recipient_count Int @default(0) @db.SmallInt + delivered_count Int @default(0) @db.SmallInt + teams teams @relation(fields: [team_id], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "announcements_team_id_fkey1") + + @@schema("public") +} + /// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. model events { id String @id(map: "announcements_pkey") @default(dbgenerated("gen_random_uuid()")) @db.Uuid @@ -461,6 +451,63 @@ model events { @@schema("public") } +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model members { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + email String @unique + full_name String? + created_at DateTime? @default(now()) @db.Timestamptz(6) + team_members team_members[] + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model profiles { + id String @id @db.Uuid + email String + full_name String? + avatar_url String? + google_refresh_token String? + created_at DateTime? @default(now()) @db.Timestamptz(6) + users users @relation(fields: [id], references: [id], onDelete: Cascade, onUpdate: NoAction) + teams teams[] + + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model team_members { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + team_id String @db.Uuid + member_id String @db.Uuid + added_at DateTime? @default(now()) @db.Timestamptz(6) + created_by String? @db.Uuid + members members @relation(fields: [member_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + teams teams @relation(fields: [team_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@unique([team_id, member_id]) + @@index([member_id], map: "idx_team_members_member_id") + @@index([team_id], map: "idx_team_members_team_id") + @@schema("public") +} + +/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info. +model teams { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + leader_id String @db.Uuid + name String + google_calendar_id String? + created_at DateTime? @default(now()) @db.Timestamptz(6) + announcements announcements[] + events events[] + team_members team_members[] + profiles profiles @relation(fields: [leader_id], references: [id], onDelete: Cascade, onUpdate: NoAction) + + @@index([leader_id], map: "idx_teams_leader_id") + @@schema("public") +} + enum aal_level { aal1 aal2 From a9e770a6f949376e0a0e7314506d855e2297656e Mon Sep 17 00:00:00 2001 From: jacob-cob-null Date: Thu, 12 Feb 2026 11:46:55 +0800 Subject: [PATCH 2/5] style: made dashboard and teams page more responsive --- app/(auth)/login/page.tsx | 3 +- app/dashboard/components/NewTeamBtn.tsx | 12 +++---- app/dashboard/components/WelcomeMsg.tsx | 23 ++++++------ .../components/team-table/TeamTable.tsx | 36 ++++++++++--------- app/dashboard/page.tsx | 12 +++---- app/dashboard/teams/[teamsId]/TeamHeader.tsx | 11 +++--- app/dashboard/teams/[teamsId]/page.tsx | 4 +-- components/ui/DataTable.tsx | 36 ++++++++++--------- 8 files changed, 72 insertions(+), 65 deletions(-) diff --git a/app/(auth)/login/page.tsx b/app/(auth)/login/page.tsx index 86a793f..1125817 100644 --- a/app/(auth)/login/page.tsx +++ b/app/(auth)/login/page.tsx @@ -13,10 +13,11 @@ export default function Page() { // Underline effect useEffect(() => { if (emphasisRef.current) { + const isMobile = window.innerWidth < 640; const annotation = annotate(emphasisRef.current, { type: "underline", padding: -3, - strokeWidth: 3, + strokeWidth: isMobile ? 2 : 3, color: "#2563eb", }); annotation.show(); diff --git a/app/dashboard/components/NewTeamBtn.tsx b/app/dashboard/components/NewTeamBtn.tsx index e86fa6d..0610929 100644 --- a/app/dashboard/components/NewTeamBtn.tsx +++ b/app/dashboard/components/NewTeamBtn.tsx @@ -45,18 +45,18 @@ function NewTeamBtn() { return ( - - - + + - + Create New Team diff --git a/app/dashboard/components/WelcomeMsg.tsx b/app/dashboard/components/WelcomeMsg.tsx index dfbb49e..55d34cc 100644 --- a/app/dashboard/components/WelcomeMsg.tsx +++ b/app/dashboard/components/WelcomeMsg.tsx @@ -18,10 +18,11 @@ function WelcomeMsg({ name, avatarUrl, email }: WelcomeMsgProps) { useEffect(() => { if (nameRef.current) { + const isMobile = window.innerWidth < 640; const annotation = annotate(nameRef.current, { type: "underline", padding: -3, - strokeWidth: 3, + strokeWidth: isMobile ? 2 : 3, color: "#2563eb", }); annotation.show(); @@ -33,31 +34,31 @@ function WelcomeMsg({ name, avatarUrl, email }: WelcomeMsgProps) { }; return ( -
-

+
+

Welcome back,{" "} {name}

-
-
+
+
{`Profile - + {email}
diff --git a/app/dashboard/components/team-table/TeamTable.tsx b/app/dashboard/components/team-table/TeamTable.tsx index be03384..038795f 100644 --- a/app/dashboard/components/team-table/TeamTable.tsx +++ b/app/dashboard/components/team-table/TeamTable.tsx @@ -69,23 +69,25 @@ export default function TeamTable({ data = sampleData }: TeamTableProps) { return ( <> - - data={data} - columns={columns} - searchKey="name" - searchPlaceholder="Search teams..." - emptyMessage="Try adding a new team!" - onRowClick={(team) => router.push(`/dashboard/teams/${team.id}`)} - renderActions={(team) => ( - handleEditClick(team)} - onDelete={() => handleDeleteClick(team)} - /> - )} - SearchComponent={SearchInput} - PaginationComponent={Pagination} - /> +
+ + data={data} + columns={columns} + searchKey="name" + searchPlaceholder="Search teams..." + emptyMessage="Try adding a new team!" + onRowClick={(team) => router.push(`/dashboard/teams/${team.id}`)} + renderActions={(team) => ( + handleEditClick(team)} + onDelete={() => handleDeleteClick(team)} + /> + )} + SearchComponent={SearchInput} + PaginationComponent={Pagination} + /> +
+
{/* Welcome Section */} -
+
@@ -32,13 +32,13 @@ export default async function Page() { {/* Team Selection Card */} -
-
+
+
-

+

Select a Team

-

+

Choose a workspace to continue your progress.

diff --git a/app/dashboard/teams/[teamsId]/TeamHeader.tsx b/app/dashboard/teams/[teamsId]/TeamHeader.tsx index 4e4af62..2d199cb 100644 --- a/app/dashboard/teams/[teamsId]/TeamHeader.tsx +++ b/app/dashboard/teams/[teamsId]/TeamHeader.tsx @@ -16,10 +16,11 @@ export function TeamHeader({ teamName }: TeamHeaderProps) { useEffect(() => { if (teamNameRef.current) { + const isMobile = window.innerWidth < 640; const annotation = annotate(teamNameRef.current, { type: "underline", padding: -3, - strokeWidth: 3, + strokeWidth: isMobile ? 2 : 3, color: "#2563eb", }); annotation.show(); @@ -27,10 +28,10 @@ export function TeamHeader({ teamName }: TeamHeaderProps) { }, []); return ( -
+

Team{" "} @@ -38,9 +39,9 @@ export function TeamHeader({ teamName }: TeamHeaderProps) {

- diff --git a/app/dashboard/teams/[teamsId]/page.tsx b/app/dashboard/teams/[teamsId]/page.tsx index 663785a..258ef3d 100644 --- a/app/dashboard/teams/[teamsId]/page.tsx +++ b/app/dashboard/teams/[teamsId]/page.tsx @@ -13,9 +13,9 @@ async function TeamPage({ params }: { params: Promise<{ teamsId: string }> }) { return (
-
+
{/* Team Dashboard Card */} -
+
{/* Header */} diff --git a/components/ui/DataTable.tsx b/components/ui/DataTable.tsx index bf7adc2..643ba21 100644 --- a/components/ui/DataTable.tsx +++ b/components/ui/DataTable.tsx @@ -82,17 +82,17 @@ export function DataTable>({ return (
-
- {SearchComponent && ( -
- - {headerActions && ( -
{headerActions}
- )} -
- )} + {SearchComponent && ( +
+ + {headerActions && ( +
{headerActions}
+ )} +
+ )} -
+
+
@@ -150,13 +150,15 @@ export function DataTable>({ {PaginationComponent && ( - 1} - canNext={currentPage < totalPages} - onPrevious={() => setCurrentPage((p) => Math.max(1, p - 1))} - onNext={() => setCurrentPage((p) => Math.min(totalPages, p + 1))} - /> +
+ 1} + canNext={currentPage < totalPages} + onPrevious={() => setCurrentPage((p) => Math.max(1, p - 1))} + onNext={() => setCurrentPage((p) => Math.min(totalPages, p + 1))} + /> +
)} ); From ac34d0eaacc5d0b4402c8f75dee68eb8d04a9669 Mon Sep 17 00:00:00 2001 From: jacob-cob-null Date: Thu, 12 Feb 2026 11:54:59 +0800 Subject: [PATCH 3/5] style: updated responsiveness of calendar event buttons --- .../components/CreateAnnouncementDialog.tsx | 5 +- .../(calendar)/components/EventModal.tsx | 73 +++++++++++++------ 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx b/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx index f3e6858..174813d 100644 --- a/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx +++ b/app/dashboard/teams/[teamsId]/(announcements)/components/CreateAnnouncementDialog.tsx @@ -80,7 +80,10 @@ export default function CreateAnnouncementDialog({ - Create Announcement + + + Create Announcement +
diff --git a/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx b/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx index 5742b52..eb654b1 100644 --- a/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx +++ b/app/dashboard/teams/[teamsId]/(calendar)/components/EventModal.tsx @@ -20,6 +20,7 @@ import { import { format } from "date-fns"; import { inter, instrumentSerif } from "@/app/fonts"; import toast from "react-hot-toast"; +import { Plus, Save, Trash2, X, CalendarPlus, CalendarCog } from "lucide-react"; interface EventModalProps { isOpen: boolean; @@ -117,15 +118,25 @@ export default function EventModal({ return ( - + - {mode === "create" ? "📅 Add New Event" : "📝 Edit Event"} + {mode === "create" ? ( + <> + + Add New Event + + ) : ( + <> + + Edit Event + + )} {mode === "create" ? "Enter the details for the new team event." @@ -135,7 +146,7 @@ export default function EventModal({
- + setTitle(e.target.value)} @@ -187,26 +198,40 @@ export default function EventModal({ )}
- - {mode === "edit" && onDelete && ( - - )} +
- - + )} + {mode === "edit" && onDelete && ( + + )} +
From 38e5e415b26f62d832d62006284066f2d3155cfd Mon Sep 17 00:00:00 2001 From: jacob-cob-null Date: Thu, 12 Feb 2026 12:19:38 +0800 Subject: [PATCH 4/5] style(announcement): refined badges, added legend to mobile view --- .../components/AnnouncementCard.tsx | 22 ++++++---- .../components/AnnouncementsClient.tsx | 25 +++++++++-- .../components/EmailStatusBadge.tsx | 42 +++++++++---------- .../teams/[teamsId]/TeamDashboardClient.tsx | 2 +- 4 files changed, 56 insertions(+), 35 deletions(-) diff --git a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx index cd55aaa..772cd3d 100644 --- a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx +++ b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementCard.tsx @@ -83,19 +83,23 @@ export default function AnnouncementCard({ return ( <> -
+
+ {/* Badge - Top Right */} +
+ +
+ {/* Content */} -
-
+
+

{announcement.title}

-

{format(new Date(announcement.created_at), "MMM d, yyyy")} diff --git a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx index ca57c90..b08ac49 100644 --- a/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx +++ b/app/dashboard/teams/[teamsId]/(announcements)/components/AnnouncementsClient.tsx @@ -97,11 +97,11 @@ export default function AnnouncementsClient({ } return ( -

+
{/* Header */} -
+
-

+

Announcements

@@ -114,6 +114,25 @@ export default function AnnouncementsClient({ />

+ {/* Mobile Legend */} +
+
+

Legend:

+ + + Sent + + + ! + Partial + + + + Failed + +
+
+ {/* Announcements List */} {announcements.length === 0 ? (
diff --git a/app/dashboard/teams/[teamsId]/(announcements)/components/EmailStatusBadge.tsx b/app/dashboard/teams/[teamsId]/(announcements)/components/EmailStatusBadge.tsx index fbd8afa..56f0ae2 100644 --- a/app/dashboard/teams/[teamsId]/(announcements)/components/EmailStatusBadge.tsx +++ b/app/dashboard/teams/[teamsId]/(announcements)/components/EmailStatusBadge.tsx @@ -1,4 +1,5 @@ import { EmailStatus } from "@prisma/client"; +import { Check, X } from "lucide-react"; interface EmailStatusBadgeProps { status: EmailStatus | null; @@ -13,64 +14,61 @@ export default function EmailStatusBadge({ deliveredCount = 0, errorMessage, }: EmailStatusBadgeProps) { - if (!status) { - return ( - - Unknown - - ); + // Don't show badge if not sent yet + if (!status || status === EmailStatus.PENDING) { + return null; } switch (status) { - case EmailStatus.PENDING: - return ( - - Not Sent - - ); case EmailStatus.SENDING: return ( - +
- Sending... + Sending... ); case EmailStatus.SENT: return ( - ✓ Sent to {recipientCount}{" "} - {recipientCount === 1 ? "member" : "members"} + + ✓ Sent to {recipientCount}{" "} + {recipientCount === 1 ? "member" : "members"} + {recipientCount} ); case EmailStatus.PARTIALLY_FAILED: return ( - ⚠ Sent to {deliveredCount} of {recipientCount} + ! + ⚠ Sent to {deliveredCount} of {recipientCount} + {deliveredCount}/{recipientCount} ); case EmailStatus.FAILED: return ( - ✗ Failed + + ✗ Failed + ); default: return ( - + {status} ); diff --git a/app/dashboard/teams/[teamsId]/TeamDashboardClient.tsx b/app/dashboard/teams/[teamsId]/TeamDashboardClient.tsx index 0986aa9..46eee58 100644 --- a/app/dashboard/teams/[teamsId]/TeamDashboardClient.tsx +++ b/app/dashboard/teams/[teamsId]/TeamDashboardClient.tsx @@ -73,7 +73,7 @@ export default function TeamDashboard({ teamId }: TeamDashboardClientProps) {
From ff396b4c4b682f5aa5b1ff6bc7bd0e3f0d08d525 Mon Sep 17 00:00:00 2001 From: jacob-cob-null Date: Thu, 12 Feb 2026 12:20:02 +0800 Subject: [PATCH 5/5] fix(prisma): fixed mismatch prisma tables after migration --- actions/(announcements)/announcements.ts | 20 ++++++++++---------- actions/(announcements)/crud.ts | 4 ++-- actions/members.ts | 10 +++++----- actions/teams.ts | 7 +++++++ 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/actions/(announcements)/announcements.ts b/actions/(announcements)/announcements.ts index 396d651..49446b1 100644 --- a/actions/(announcements)/announcements.ts +++ b/actions/(announcements)/announcements.ts @@ -34,11 +34,11 @@ export async function sendAnnouncement( const announcement = await prisma.announcements.findUnique({ where: { id: announcementId }, include: { - team: { + teams: { include: { - teamMembers: { + team_members: { include: { - member: true, + members: true, }, }, }, @@ -51,7 +51,7 @@ export async function sendAnnouncement( } // Verify user is team leader - if (announcement.team.leader_id !== leaderId) { + if (announcement.teams.leader_id !== leaderId) { return { success: false, error: "Unauthorized: Not team leader" }; } @@ -85,9 +85,9 @@ export async function sendAnnouncement( } // Fetch team members - const recipients = announcement.team.teamMembers.map((tm) => ({ - email: tm.member.email, - name: tm.member.full_name || undefined, + const recipients = announcement.teams.team_members.map((tm) => ({ + email: tm.members.email, + name: tm.members.full_name || undefined, })); if (recipients.length === 0) { @@ -109,7 +109,7 @@ export async function sendAnnouncement( recipients, title: announcement.title, content: announcement.content, - teamName: announcement.team.name, + teamName: announcement.teams.name, }); // Store batch IDs (as JSON array if multiple batches) @@ -177,7 +177,7 @@ export async function getAnnouncementStatus(announcementId: bigint) { const announcement = await prisma.announcements.findUnique({ where: { id: announcementId }, include: { - team: true, + teams: true, }, }); @@ -185,7 +185,7 @@ export async function getAnnouncementStatus(announcementId: bigint) { return { success: false, error: "Announcement not found" }; } - if (announcement.team.leader_id !== leaderId) { + if (announcement.teams.leader_id !== leaderId) { return { success: false, error: "Unauthorized" }; } diff --git a/actions/(announcements)/crud.ts b/actions/(announcements)/crud.ts index 75b23ab..eba9a38 100644 --- a/actions/(announcements)/crud.ts +++ b/actions/(announcements)/crud.ts @@ -120,7 +120,7 @@ export async function deleteAnnouncement(announcementId: bigint) { // Fetch announcement with team const announcement = await prisma.announcements.findUnique({ where: { id: announcementId }, - include: { team: true }, + include: { teams: true }, }); if (!announcement) { @@ -128,7 +128,7 @@ export async function deleteAnnouncement(announcementId: bigint) { } // Verify team ownership - if (announcement.team.leader_id !== leaderId) { + if (announcement.teams.leader_id !== leaderId) { return { success: false, error: "Unauthorized: Not team leader" }; } diff --git a/actions/members.ts b/actions/members.ts index 6cca733..1211cfd 100644 --- a/actions/members.ts +++ b/actions/members.ts @@ -234,7 +234,7 @@ export async function getMembersForTeam(teamId: string) { const teamMembers = await prisma.team_members.findMany({ where: { team_id: teamId }, include: { - member: true, + members: true, }, orderBy: { added_at: "desc", @@ -242,9 +242,9 @@ export async function getMembersForTeam(teamId: string) { }); const members = teamMembers.map((tm) => ({ - id: tm.member.id, - email: tm.member.email, - full_name: tm.member.full_name, + id: tm.members.id, + email: tm.members.email, + full_name: tm.members.full_name, added_at: tm.added_at, })); @@ -273,7 +273,7 @@ export async function updateMember( const teamMembership = await prisma.team_members.findFirst({ where: { member_id: memberId, - team: { + teams: { leader_id: leaderId, }, }, diff --git a/actions/teams.ts b/actions/teams.ts index be2711c..e6a2ac7 100644 --- a/actions/teams.ts +++ b/actions/teams.ts @@ -120,6 +120,13 @@ export async function deleteTeam(teamId: string) { } try { + // Delete announcements first + await prisma.announcements.deleteMany({ + where: { + team_id: teamId, + }, + }); + // Delete calendar const calendarResult = await deleteTeamCalendar(teamId); if (!calendarResult.success) {