Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/dashboard-frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export function DashboardFrame({ initialSession }: { initialSession: AdminSessio
if (result.error) throw new Error(result.error.message)
await router.invalidate()
await router.navigate({ to: "/login", replace: true })
} catch {
} catch (error) {
console.error(error)
toast.error("Could not sign out. Please try again.")
setLoggingOut(false)
}
Expand Down
6 changes: 4 additions & 2 deletions src/components/dashboard-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ export function DashboardSidebar({ user, loggingOut, onLogout, ...props }: Dashb
try {
const stored = window.localStorage.getItem(categoryStorageKey)
if (stored) setCategoryState(JSON.parse(stored))
} catch {
} catch (error) {
console.error(error)
// Ignore malformed or unavailable local storage and use route-based defaults.
}
}, [])
Expand All @@ -66,7 +67,8 @@ export function DashboardSidebar({ user, loggingOut, onLogout, ...props }: Dashb
const next = { ...current, [title]: open }
try {
window.localStorage.setItem(categoryStorageKey, JSON.stringify(next))
} catch {
} catch (error) {
console.error(error)
// The in-memory state still works when storage is unavailable.
}
return next
Expand Down
4 changes: 3 additions & 1 deletion src/components/telegram/create-grant-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -266,14 +266,16 @@ export function CreateGrantDialog({ user: fixedUser }: CreateGrantDialogProps) {
data: { userId: selectedUser.id, since, until, reason: reason.trim() || undefined },
})
if (result.error) {
console.error(result.error)
toast.error(grantMutationError(result.error))
return
}
toast.success(`Grant created for ${displayName(selectedUser)}.`)
closeAndReset()
try {
await router.invalidate({ sync: true })
} catch {
} catch (error) {
console.error(error)
toast.warning("The grant was created, but the latest grants could not be refreshed.")
}
} catch (error) {
Expand Down
56 changes: 38 additions & 18 deletions src/features/account/use-account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ export function useAccount(initialSession: AdminSession) {
try {
const [passkeyResult, sessionResult] = await Promise.all([auth.passkey.listUserPasskeys(), auth.listSessions()])
if (passkeyResult.error || sessionResult.error) {
if (passkeyResult.error) console.error(passkeyResult.error)
if (sessionResult.error) console.error(sessionResult.error)
setSecurityError("Could not load passkeys and active sessions. Your existing security data is still shown.")
return false
}
setPasskeys(passkeyResult.data ?? [])
setSessions(sessionResult.data ?? [])
setSecurityError("")
return true
} catch {
} catch (error) {
console.error(error)
setSecurityError("Could not load passkeys and active sessions. Your existing security data is still shown.")
return false
} finally {
Expand All @@ -58,12 +61,15 @@ export function useAccount(initialSession: AdminSession) {
setNotice(null)
try {
const result = await auth.updateUser({ name: name.trim() })
if (result.error) setNotice({ type: "error", text: result.error.message ?? "Could not update your name." })
else {
if (result.error) {
console.error(result.error)
setNotice({ type: "error", text: result.error.message ?? "Could not update your name." })
} else {
await sessionQuery.refetch()
setNotice({ type: "success", text: "Profile name updated." })
}
} catch {
} catch (error) {
console.error(error)
setNotice({ type: "error", text: "Could not update your name." })
} finally {
setBusy(null)
Expand All @@ -85,7 +91,8 @@ export function useAccount(initialSession: AdminSession) {
await uploadProfilePictureFn({ data: formData })
await sessionQuery.refetch()
setNotice({ type: "success", text: "Profile picture updated." })
} catch {
} catch (error) {
console.error(error)
setNotice({ type: "error", text: "Could not update your profile picture." })
}
setBusy(null)
Expand All @@ -96,12 +103,15 @@ export function useAccount(initialSession: AdminSession) {
setNotice(null)
try {
const result = await auth.updateUser({ image: null })
if (result.error) setNotice({ type: "error", text: result.error.message ?? "Could not remove the picture." })
else {
if (result.error) {
console.error(result.error)
setNotice({ type: "error", text: result.error.message ?? "Could not remove the picture." })
} else {
await sessionQuery.refetch()
setNotice({ type: "success", text: "Profile picture removed." })
}
} catch {
} catch (error) {
console.error(error)
setNotice({ type: "error", text: "Could not remove the picture." })
} finally {
setBusy(null)
Expand All @@ -113,15 +123,18 @@ export function useAccount(initialSession: AdminSession) {
setNotice(null)
try {
const result = await auth.passkey.addPasskey({ name: `Passkey ${passkeys.length + 1}` })
if (result.error) setNotice({ type: "error", text: result.error.message ?? "Could not create the passkey." })
else {
if (result.error) {
console.error(result.error)
setNotice({ type: "error", text: result.error.message ?? "Could not create the passkey." })
} else {
const refreshed = await refreshSecurityData()
setNotice({
type: "success",
text: refreshed ? "Passkey created." : "Passkey created. Refresh security data to see the updated list.",
})
}
} catch {
} catch (error) {
console.error(error)
setNotice({ type: "error", text: "Could not create the passkey." })
} finally {
setBusy(null)
Expand All @@ -133,15 +146,18 @@ export function useAccount(initialSession: AdminSession) {
setNotice(null)
try {
const result = await auth.passkey.deletePasskey({ id })
if (result.error) setNotice({ type: "error", text: result.error.message ?? "Could not delete the passkey." })
else {
if (result.error) {
console.error(result.error)
setNotice({ type: "error", text: result.error.message ?? "Could not delete the passkey." })
} else {
const refreshed = await refreshSecurityData()
setNotice({
type: "success",
text: refreshed ? "Passkey deleted." : "Passkey deleted. Refresh security data to see the updated list.",
})
}
} catch {
} catch (error) {
console.error(error)
setNotice({ type: "error", text: "Could not delete the passkey." })
} finally {
setBusy(null)
Expand All @@ -153,8 +169,10 @@ export function useAccount(initialSession: AdminSession) {
setNotice(null)
try {
const result = await auth.revokeOtherSessions()
if (result.error) setNotice({ type: "error", text: result.error.message ?? "Could not revoke other sessions." })
else {
if (result.error) {
console.error(result.error)
setNotice({ type: "error", text: result.error.message ?? "Could not revoke other sessions." })
} else {
const refreshed = await refreshSecurityData()
setNotice({
type: "success",
Expand All @@ -163,7 +181,8 @@ export function useAccount(initialSession: AdminSession) {
: "Sessions were signed out. Refresh security data to see the updated list.",
})
}
} catch {
} catch (error) {
console.error(error)
setNotice({ type: "error", text: "Could not revoke other sessions." })
} finally {
setBusy(null)
Expand All @@ -178,7 +197,8 @@ export function useAccount(initialSession: AdminSession) {
if (result.error) throw new Error(result.error.message)
await router.invalidate()
await router.navigate({ to: "/login", replace: true })
} catch {
} catch (error) {
console.error(error)
setNotice({ type: "error", text: "Could not sign out. Please try again." })
setBusy(null)
}
Expand Down
6 changes: 4 additions & 2 deletions src/features/associations/association-dialogs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { Textarea } from "@/components/ui/textarea"
import { errorHasCode } from "@/lib/errors"
import { ASSOCIATION_LOGO_MAX_SIZE, ASSOCIATION_LOGO_TYPES, getAssociationInitials } from "./associations.constants"
import { createAssociation, deleteAssociation, editAssociation } from "./associations.functions"
import { associationSaveErrorMessage } from "./associations.validation"
Expand Down Expand Up @@ -93,6 +94,7 @@ export function AssociationDialog({
const saved = editing ? await editAssociationFn({ data }) : await createAssociationFn({ data })
onSaved(saved, dialog.mode)
} catch (cause) {
console.error(cause)
setError(associationSaveErrorMessage(cause))
} finally {
setPending(false)
Expand Down Expand Up @@ -213,8 +215,8 @@ export function DeleteAssociationDialog({
await deleteAssociationFn({ data: { id: association.id } })
onDeleted(association.id)
} catch (cause) {
const message = cause instanceof Error ? cause.message : ""
if (message.includes("NOT_FOUND")) onDeleted(association.id)
console.error(cause)
if (errorHasCode(cause, "NOT_FOUND")) onDeleted(association.id)
else toast.error("The association could not be deleted. Check your permissions and try again.")
} finally {
setPending(false)
Expand Down
5 changes: 3 additions & 2 deletions src/features/associations/association-links-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from "@/components/ui/dialog"
import { Field, FieldError, FieldLabel } from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { errorHasCode } from "@/lib/errors"
import { ASSOCIATION_LINK_FIELDS } from "./associations.constants"
import { editAssociationLinks } from "./associations.functions"
import type { Association, AssociationLinks } from "./types"
Expand Down Expand Up @@ -46,9 +47,9 @@ export function AssociationLinksDialog({
try {
onSaved(await editLinksFn({ data: { id: association.id, links: normalizeLinks(links) } }))
} catch (cause) {
const message = cause instanceof Error ? cause.message : ""
console.error(cause)
setError(
message.includes("NOT_FOUND")
errorHasCode(cause, "NOT_FOUND")
? "This association no longer exists."
: "The links could not be saved. Check the values and your permissions."
)
Expand Down
3 changes: 2 additions & 1 deletion src/features/associations/associations-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ export function AssociationsPage({ loadedAssociations }: { loadedAssociations: A
async function refresh() {
try {
await router.invalidate({ sync: true })
} catch {
} catch (error) {
console.error(error)
toast.warning("Your change was saved, but the association list could not be refreshed.")
}
}
Expand Down
17 changes: 10 additions & 7 deletions src/features/associations/associations.validation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from "zod"
import { errorHasCode } from "../../lib/errors.ts"
import { ASSOCIATION_LOGO_MAX_SIZE, ASSOCIATION_LOGO_TYPES } from "./associations.constants.ts"

const ALLOWED_LOGO_TYPES = new Set<string>(ASSOCIATION_LOGO_TYPES)
Expand Down Expand Up @@ -64,17 +65,19 @@ export const associationLinksInput = z.object({
export const associationIdInput = z.object({ id: z.number().int().positive() })

export function associationSaveErrorMessage(cause: unknown) {
const message = cause instanceof Error ? cause.message : ""

if (message.includes("NOT_FOUND")) return "This association no longer exists."
if (message.includes("INVALID_NAME")) return "Enter an association name no longer than 200 characters."
if (message.includes("INVALID_DESCRIPTIONIT")) {
if (errorHasCode(cause, "NOT_FOUND")) return "This association no longer exists."
if (errorHasCode(cause, "INVALID_NAME")) return "Enter an association name no longer than 200 characters."
if (errorHasCode(cause, "INVALID_DESCRIPTIONIT")) {
return "Enter an Italian description no longer than 20,000 characters."
}
if (message.includes("INVALID_DESCRIPTIONEN")) {
if (errorHasCode(cause, "INVALID_DESCRIPTIONEN")) {
return "Enter an English description no longer than 20,000 characters."
}
if (message.includes("LOGO") || message.includes("file")) {
if (
errorHasCode(cause, "LOGO_TOO_LARGE") ||
errorHasCode(cause, "INVALID_LOGO_TYPE") ||
errorHasCode(cause, "INVALID_FILE_TYPE")
) {
return "Choose a JPG, PNG, or SVG logo no larger than 1 MB."
}

Expand Down
3 changes: 2 additions & 1 deletion src/features/auth/auth.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ export const testBackend = createServerFn()
try {
await context.backend.test.dbQuery.query({ dbName: "web" })
return true
} catch {
} catch (error) {
console.error(error)
return false
}
})
24 changes: 18 additions & 6 deletions src/features/auth/login-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ export function LoginPage() {
try {
const { data, error } = await auth.emailOtp.sendVerificationOtp({ type: "sign-in", email: email.trim() })
if (data?.success) setSent(true)
else setNotice(error?.message ?? "We could not send a code. Check your email and try again.")
} catch {
else {
if (error) console.error(error)
setNotice(error?.message ?? "We could not send a code. Check your email and try again.")
}
} catch (error) {
console.error(error)
setNotice("We could not reach the authentication service. Please try again.")
} finally {
setBusy(false)
Expand All @@ -41,8 +45,12 @@ export function LoginPage() {
try {
const { data, error } = await auth.signIn.emailOtp({ email: email.trim(), otp })
if (data) await router.navigate({ to: "/dashboard" })
else setNotice(error?.message ?? "That code is not valid. Please try again.")
} catch {
else {
if (error) console.error(error)
setNotice(error?.message ?? "That code is not valid. Please try again.")
}
} catch (error) {
console.error(error)
setNotice("We could not verify the code. Check your connection and try again.")
} finally {
setBusy(false)
Expand All @@ -55,8 +63,12 @@ export function LoginPage() {
try {
const { data, error } = await auth.signIn.passkey()
if (data) await router.navigate({ to: "/dashboard" })
else setNotice(error?.message ?? "Passkey sign in was cancelled or unavailable.")
} catch {
else {
if (error) console.error(error)
setNotice(error?.message ?? "Passkey sign in was cancelled or unavailable.")
}
} catch (error) {
console.error(error)
setNotice("Passkey sign in is unavailable right now. Please try again.")
} finally {
setBusy(false)
Expand Down
4 changes: 3 additions & 1 deletion src/features/azure/group-membership.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ function MembershipDialog({
const input = { data: { groupId: group.id, userId: selectedMember.id } }
const result = adding ? await addGroupMember(input) : await removeGroupMember(input)
if (result.error) {
console.error(result.error)
toast.error(mutationErrorMessage(result.error))
return
}
Expand All @@ -145,7 +146,8 @@ function MembershipDialog({
setConfirmRemoval(false)
try {
await router.invalidate({ sync: true })
} catch {
} catch (error) {
console.error(error)
toast.warning("The membership was updated, but the latest group data could not be refreshed.")
}
} catch (error) {
Expand Down
3 changes: 2 additions & 1 deletion src/features/azure/member-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export function MemberDialog({
await createMember({ data: { firstName, lastName, assocNumber, sendEmailTo: email } })
await onSaved("create")
}
} catch {
} catch (error) {
console.error(error)
rollback?.()
setError("The member could not be saved. Check the values and your permissions.")
setPending(false)
Expand Down
3 changes: 2 additions & 1 deletion src/features/azure/members-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ export function AzureMembersPage({ initialMembers }: { initialMembers: AzureMemb
if (mode === "create") {
try {
await router.invalidate({ sync: true })
} catch {
} catch (error) {
console.error(error)
toast.warning("The member was created, but the latest directory data could not be refreshed.")
}
}
Expand Down
Loading
Loading