Skip to content

Commit

Permalink
feat: Display warning when access-code is being removed
Browse files Browse the repository at this point in the history
  • Loading branch information
louis-pre committed Jul 24, 2024
1 parent 3970632 commit cb29667
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 25 deletions.
5 changes: 3 additions & 2 deletions src/lib/seam/access-codes/use-access-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ export type UseAccessCodeParams = AccessCodesGetParams
export type UseAccessCodeData = AccessCode | null

export function useAccessCode(
params: UseAccessCodeParams
params: UseAccessCodeParams,
isEnabled: boolean = true
): UseSeamQueryResult<'accessCode', UseAccessCodeData> {
const { client } = useSeamClient()
const { data, ...rest } = useQuery<UseAccessCodeData, SeamHttpApiError>({
enabled: client != null,
enabled: client != null && isEnabled,
queryKey: ['access_codes', 'get', params],
queryFn: async () => {
if (client == null) return null
Expand Down
32 changes: 28 additions & 4 deletions src/lib/seam/components/AccessCodeDetails/AccessCodeDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { useIsDateInPast } from 'lib/ui/use-is-date-in-past.js'
export interface AccessCodeDetailsProps extends CommonProps {
accessCodeId: string
onEdit: () => void
onDelete: () => void
isBeingRemoved?: boolean
}

export const NestedAccessCodeDetails =
Expand All @@ -35,6 +37,7 @@ export const NestedAccessCodeDetails =
export function AccessCodeDetails({
accessCodeId,
onEdit,
onDelete,
errorFilter = () => true,
warningFilter = () => true,
disableCreateAccessCode = false,
Expand All @@ -44,12 +47,16 @@ export function AccessCodeDetails({
disableResourceIds = false,
disableConnectedAccountInformation = false,
disableClimateSettingSchedules,
isBeingRemoved,
onBack,
className,
}: AccessCodeDetailsProps): JSX.Element | null {
useComponentTelemetry('AccessCodeDetails')

const { accessCode } = useAccessCode({ access_code_id: accessCodeId })
const { accessCode } = useAccessCode(
{ access_code_id: accessCodeId },
isBeingRemoved !== true
)
const [selectedDeviceId, selectDevice] = useState<string | null>(null)
const { mutate: deleteCode, isPending: isDeleting } = useDeleteAccessCode()

Expand Down Expand Up @@ -96,6 +103,15 @@ export function AccessCodeDetails({
variant: 'warning' as const,
message: warning.message,
})),

...(isBeingRemoved === true
? [
{
variant: 'warning' as const,
message: t.warningRemoving,
},
]
: []),
]

return (
Expand Down Expand Up @@ -134,17 +150,24 @@ export function AccessCodeDetails({
{(!disableEditAccessCode || !disableDeleteAccessCode) && (
<div className='seam-actions'>
{!disableEditAccessCode && (
<Button size='small' onClick={onEdit} disabled={isDeleting}>
<Button
size='small'
onClick={onEdit}
disabled={isBeingRemoved === true || isDeleting}
>
{t.editCode}
</Button>
)}
{!disableDeleteAccessCode && (
<Button
size='small'
onClick={() => {
deleteCode({ access_code_id: accessCode.access_code_id })
deleteCode(
{ access_code_id: accessCode.access_code_id },
{ onSuccess: onDelete }
)
}}
disabled={isDeleting}
disabled={isBeingRemoved === true || isDeleting}
>
{t.deleteCode}
</Button>
Expand Down Expand Up @@ -282,4 +305,5 @@ const t = {
at: 'at',
editCode: 'Edit code',
deleteCode: 'Delete code',
warningRemoving: 'This access code is currently being removed.',
}
13 changes: 10 additions & 3 deletions src/lib/seam/components/AccessCodeTable/AccessCodeMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useToggle } from 'lib/ui/use-toggle.js'
export interface AccessCodeMenuProps {
accessCode: AccessCode
onEdit: () => void
onDelete: () => void
onViewDetails: () => void
disableEditAccessCode: boolean
disableDeleteAccessCode: boolean
Expand Down Expand Up @@ -49,6 +50,7 @@ function Content({
disableEditAccessCode,
disableDeleteAccessCode,
onEdit,
onDelete,
deleteConfirmationVisible,
toggleDeleteConfirmation,
}: AccessCodeMenuProps): JSX.Element {
Expand All @@ -66,9 +68,14 @@ function Content({
variant='solid'
disabled={deleteAccessCode.isPending}
onClick={() => {
deleteAccessCode.mutate({
access_code_id: accessCode.access_code_id,
})
deleteAccessCode.mutate(
{
access_code_id: accessCode.access_code_id,
},
{
onSuccess: onDelete,
}
)
}}
>
{t.confirmDelete}
Expand Down
40 changes: 28 additions & 12 deletions src/lib/seam/components/AccessCodeTable/AccessCodeRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface AccessCodeRowProps {
accessCode: AccessCode
onClick: () => void
onEdit: () => void
onDelete: () => void
disableEditAccessCode: boolean
disableDeleteAccessCode: boolean
}
Expand All @@ -21,25 +22,39 @@ export function AccessCodeRow({
onClick,
accessCode,
onEdit,
onDelete,
disableEditAccessCode,
disableDeleteAccessCode,
}: AccessCodeRowProps): JSX.Element {
const isBeingRemoved = accessCode.status === 'removing'

const errorCount = accessCode.errors.length
const warningCount = accessCode.warnings.length
const isPlural = errorCount === 0 || errorCount > 1
const errorIconTitle = isPlural
? `${errorCount} ${t.codeIssues}`
: `${errorCount} ${t.codeIssue}`
const warningIconTitle = isPlural
? `${warningCount} ${t.codeIssues}`
: `${warningCount} ${t.codeIssue}`
const warningCount = accessCode.warnings.length + (isBeingRemoved ? 1 : 0)
const errorIconTitle =
errorCount === 0 || errorCount > 1
? `${errorCount} ${t.codeIssues}`
: `${errorCount} ${t.codeIssue}`
const warningIconTitle =
warningCount === 0 || warningCount > 1
? `${warningCount} ${t.codeIssues}`
: `${warningCount} ${t.codeIssue}`

return (
<TableRow onClick={onClick}>
<TableCell className='seam-icon-cell'>
<TableCell
className='seam-icon-cell'
style={{
opacity: isBeingRemoved ? 0.4 : 1,
}}
>
<AccessCodeMainIcon accessCode={accessCode} />
</TableCell>
<TableCell className='seam-name-cell'>
<TableCell
className='seam-name-cell'
style={{
opacity: isBeingRemoved ? 0.4 : 1,
}}
>
<Title className='seam-truncated-text'>{accessCode.name}</Title>
<CodeDetails accessCode={accessCode} />
</TableCell>
Expand All @@ -63,9 +78,10 @@ export function AccessCodeRow({
<AccessCodeMenu

Check failure on line 78 in src/lib/seam/components/AccessCodeTable/AccessCodeRow.tsx

View workflow job for this annotation

GitHub Actions / Typecheck (Node.js v18)

Type '{ accessCode: { type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ ...; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }; ... 4 more .....' is missing the following properties from type 'AccessCodeMenuProps': deleteConfirmationVisible, toggleDeleteConfirmation

Check failure on line 78 in src/lib/seam/components/AccessCodeTable/AccessCodeRow.tsx

View workflow job for this annotation

GitHub Actions / Typecheck (Node.js v20)

Type '{ accessCode: { type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ ...; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }; ... 4 more .....' is missing the following properties from type 'AccessCodeMenuProps': deleteConfirmationVisible, toggleDeleteConfirmation

Check failure on line 78 in src/lib/seam/components/AccessCodeTable/AccessCodeRow.tsx

View workflow job for this annotation

GitHub Actions / Build / Package

Type '{ accessCode: { type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ ...; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }; ... 4 more .....' is missing the following properties from type 'AccessCodeMenuProps': deleteConfirmationVisible, toggleDeleteConfirmation
accessCode={accessCode}
onEdit={onEdit}
onDelete={onDelete}
onViewDetails={onClick}
disableDeleteAccessCode={disableDeleteAccessCode}
disableEditAccessCode={disableEditAccessCode}
disableDeleteAccessCode={isBeingRemoved || disableDeleteAccessCode}
disableEditAccessCode={isBeingRemoved || disableEditAccessCode}
/>
</TableCell>
</TableRow>
Expand Down
38 changes: 34 additions & 4 deletions src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export function AccessCodeTable({
const { accessCodes, isInitialLoading, isError, refetch } = useAccessCodes({
device_id: deviceId,
})
const [deletedAccessCodeIds, setDeletedAccessCodeIds] = useState<string[]>([])

const [selectedViewAccessCodeId, setSelectedViewAccessCodeId] = useState<
string | null
Expand All @@ -102,8 +103,19 @@ export function AccessCodeTable({
() =>
accessCodes
?.filter((accessCode) => accessCodeFilter(accessCode, searchInputValue))
?.map((accessCode) =>
deletedAccessCodeIds.includes(accessCode.access_code_id)
? { ...accessCode, status: 'removing' }
: accessCode
)
?.sort(accessCodeComparator) ?? [],

Check failure on line 111 in src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx

View workflow job for this annotation

GitHub Actions / Typecheck (Node.js v18)

Argument of type '(accessCodeA: { type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_acce...' is not assignable to parameter of type '(a: { status: string; type: "time_bound" | "ongoing"; code: string | null; name: string | null; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }, b: ...'.

Check failure on line 111 in src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx

View workflow job for this annotation

GitHub Actions / Typecheck (Node.js v20)

Argument of type '(accessCodeA: { type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_acce...' is not assignable to parameter of type '(a: { status: string; type: "time_bound" | "ongoing"; code: string | null; name: string | null; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }, b: ...'.

Check failure on line 111 in src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx

View workflow job for this annotation

GitHub Actions / Build / Package

Argument of type '(accessCodeA: { type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_acce...' is not assignable to parameter of type '(a: { status: string; type: "time_bound" | "ongoing"; code: string | null; name: string | null; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }, b: ...'.
[accessCodes, searchInputValue, accessCodeFilter, accessCodeComparator]
[
accessCodes,
searchInputValue,
accessCodeFilter,
accessCodeComparator,
deletedAccessCodeIds,
]
)

const handleAccessCodeClick = useCallback(
Expand All @@ -126,12 +138,16 @@ export function AccessCodeTable({
[setSelectedEditAccessCodeId]
)

const handleAccessCodeDelete = useCallback((accessCodeId: string): void => {
setDeletedAccessCodeIds((prev) => [...prev, accessCodeId])
}, [])

const [accessCodeResult, setAccessCodeResult] = useState<
'created' | 'updated' | null
>(null)

const accessCodeResultMessage =
accessCodeResult === 'created' ? t.accesCodeCreated : t.accesCodeUpdated
accessCodeResult === 'created' ? t.accessCodeCreated : t.accessCodeUpdated

if (selectedEditAccessCodeId != null) {
return (
Expand Down Expand Up @@ -174,6 +190,9 @@ export function AccessCodeTable({
onEdit={() => {
setSelectedEditAccessCodeId(selectedViewAccessCodeId)
}}
onDelete={() => {
handleAccessCodeDelete(selectedViewAccessCodeId)
}}
errorFilter={errorFilter}
warningFilter={warningFilter}
disableLockUnlock={disableLockUnlock}
Expand All @@ -185,6 +204,11 @@ export function AccessCodeTable({
disableConnectedAccountInformation
}
disableClimateSettingSchedules={disableClimateSettingSchedules}
isBeingRemoved={filteredAccessCodes.some(
(accessCode) =>
accessCode.access_code_id === selectedViewAccessCodeId &&
accessCode.status === 'removing'
)}
onBack={() => {
setSelectedViewAccessCodeId(null)
}}
Expand Down Expand Up @@ -267,6 +291,7 @@ export function AccessCodeTable({
accessCodes={filteredAccessCodes}

Check failure on line 291 in src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx

View workflow job for this annotation

GitHub Actions / Typecheck (Node.js v18)

Type '{ status: string; type: "time_bound" | "ongoing"; code: string | null; name: string | null; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }[]' is not assignable to type '{ type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: s...'.

Check failure on line 291 in src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx

View workflow job for this annotation

GitHub Actions / Typecheck (Node.js v20)

Type '{ status: string; type: "time_bound" | "ongoing"; code: string | null; name: string | null; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }[]' is not assignable to type '{ type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: s...'.

Check failure on line 291 in src/lib/seam/components/AccessCodeTable/AccessCodeTable.tsx

View workflow job for this annotation

GitHub Actions / Build / Package

Type '{ status: string; type: "time_bound" | "ongoing"; code: string | null; name: string | null; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: string | ... 1 more ... | undefined; }[]' is not assignable to type '{ type: "time_bound" | "ongoing"; code: string | null; name: string | null; status: "set" | "unknown" | "setting" | "unset" | "removing"; created_at: string; errors: ({ message: string; is_connected_account_error: true; error_code: string; } | { ...; } | { ...; })[]; ... 14 more ...; pulled_backup_access_code_id?: s...'.
onAccessCodeClick={handleAccessCodeClick}
onAccessCodeEdit={handleAccessCodeEdit}
onAccessCodeDelete={handleAccessCodeDelete}
errorFilter={errorFilter}
warningFilter={warningFilter}
disableEditAccessCode={disableEditAccessCode}
Expand Down Expand Up @@ -295,6 +320,7 @@ function Content(props: {
accessCodes: AccessCode[]
onAccessCodeClick: (accessCodeId: string) => void
onAccessCodeEdit: (accessCodeId: string) => void
onAccessCodeDelete: (accessCodeId: string) => void
errorFilter: (error: AccessCode['errors'][number]) => boolean
warningFilter: (warning: AccessCode['warnings'][number]) => boolean
disableEditAccessCode: boolean
Expand All @@ -304,6 +330,7 @@ function Content(props: {
accessCodes,
onAccessCodeClick,
onAccessCodeEdit,
onAccessCodeDelete,
errorFilter,
warningFilter,
disableEditAccessCode,
Expand Down Expand Up @@ -350,6 +377,9 @@ function Content(props: {
onEdit={() => {
onAccessCodeEdit(accessCode.access_code_id)
}}
onDelete={() => {
onAccessCodeDelete(accessCode.access_code_id)
}}
/>
))}
</>
Expand All @@ -360,8 +390,8 @@ const t = {
accessCodes: 'Access Codes',
noAccessCodesMessage: 'Sorry, no access codes were found',
loading: 'Loading access codes',
accesCodeUpdated: 'Access code updated',
accesCodeCreated: 'Access code created',
accessCodeUpdated: 'Access code updated',
accessCodeCreated: 'Access code created',
tryAgain: 'Try again',
fallbackErrorMessage: 'Access codes could not be loaded',
}

0 comments on commit cb29667

Please sign in to comment.