Skip to content

Commit e30c42f

Browse files
committed
feat: add possibility to delete an attempt on the history page
Removed globally the margin-bottom on button (override simpledotcss). Includes some stylistic changes.
1 parent 6091c67 commit e30c42f

10 files changed

Lines changed: 143 additions & 44 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { type Component, type Ref, Show, createSignal } from "solid-js";
2+
import { Dialog, type DialogRef } from "../../ui/components/dialog/dialog";
3+
import type { Attempt } from "../schemas/attempt";
4+
import { AttemptsTable } from "./attempts-table";
5+
import { DialogFooterConfirm } from "../../ui/components/dialog/footers/dialog-footer-confirm";
6+
import { css } from "@emotion/css";
7+
import { isFunction } from "remeda";
8+
9+
const sContent = css({
10+
display: "flex",
11+
flexDirection: "column",
12+
gap: "var(--mk-spacing-large)",
13+
overflow: "auto",
14+
});
15+
16+
export interface AttemptDeleteDialogRef {
17+
open: (attempt: Attempt) => void;
18+
}
19+
20+
interface AttemptDeleteDialogProps {
21+
ref?: Ref<AttemptDeleteDialogRef>;
22+
onDelete: (attempt: Attempt) => void;
23+
}
24+
25+
export const AttemptDeleteDialog: Component<AttemptDeleteDialogProps> = (props) => {
26+
let dialog!: DialogRef; // oxlint-disable-line init-declarations no-unassigned-vars
27+
28+
const [attempt, setAttempt] = createSignal<Attempt | undefined>();
29+
30+
const setDialog = (ref: DialogRef): void => {
31+
dialog = ref;
32+
if (isFunction(props.ref)) {
33+
props.ref({
34+
open: (attempt: Attempt) => {
35+
setAttempt(attempt);
36+
dialog.open();
37+
},
38+
});
39+
}
40+
};
41+
42+
const onClose = (): void => {
43+
setAttempt(undefined);
44+
};
45+
46+
const onConfirm = (attempt: Attempt): void => {
47+
props.onDelete(attempt);
48+
};
49+
50+
return (
51+
<Dialog ref={setDialog} title="Delete an attempt" onClose={onClose}>
52+
<Show when={attempt()}>
53+
{(attempt) => (
54+
<>
55+
<div class={sContent}>
56+
<span>Do you really want to delete the following attempt?</span>
57+
<AttemptsTable attempts={[attempt()]} showTime={false}></AttemptsTable>
58+
</div>
59+
<DialogFooterConfirm
60+
onConfirm={() => {
61+
onConfirm(attempt());
62+
}}
63+
/>
64+
</>
65+
)}
66+
</Show>
67+
</Dialog>
68+
);
69+
};

src/domains/attempt/components/attempts-table.tsx

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,11 @@ import type { Attempt } from "../schemas/attempt";
33
import { Cell } from "../../ui/components/grid/cell";
44
import { GridColumn } from "../../ui/components/grid/grid-column";
55
import { HorizontalDivider } from "../../ui/components/grid/horizontal-divider";
6+
import { SymbolButton } from "../../ui/components/buttons/symbol-button";
67
import { VerticalDivider } from "../../ui/components/grid/vertical-divider";
78
import { cellCss } from "../../ui/css/cell-css";
89
import { isDefined } from "remeda";
910

10-
const sFirstColumn = cellCss({
11-
extraPadding: "left",
12-
});
13-
14-
const sLastColumn = cellCss({
15-
extraPadding: "right",
16-
});
17-
1811
const sValue = cellCss({
1912
mono: true,
2013
});
@@ -32,17 +25,21 @@ interface AttemptsTableProps {
3225
showTime?: boolean;
3326
showTrack?: boolean;
3427
limit?: number;
28+
onDelete?: (attempt: Attempt) => void;
3529
}
3630

3731
export const AttemptsTable: Component<AttemptsTableProps> = (props) => {
38-
const GRID_COLUMNS = 16;
32+
const GRID_COLUMNS = 18;
3933
const GRID_SPLITS_COLUMNS = 7;
4034
const GRID_RESULT_COLUMNS = 3;
4135
const GRID_SEPARATION_THICKNESS = 2;
4236

4337
const showTime = createMemo(() => props.showTime ?? true);
4438
const showTrack = createMemo(() => props.showTrack ?? true);
45-
const gridColumns = createMemo(() => GRID_COLUMNS - (Number(!showTime()) + Number(!showTrack())));
39+
const showAction = createMemo(() => Boolean(props.onDelete));
40+
const gridColumns = createMemo(
41+
() => GRID_COLUMNS - (Number(!showTime()) + Number(!showTrack()) + 2 * Number(!showAction())),
42+
);
4643

4744
const attempts = createMemo(() => props.attempts.slice(0, props.limit));
4845

@@ -54,7 +51,7 @@ export const AttemptsTable: Component<AttemptsTableProps> = (props) => {
5451
{(attempt, aIndex) => (
5552
<>
5653
<Show when={aIndex() % 7 === 0}>
57-
<Cell text="Date" css={[sTitle, sInfo, sFirstColumn]} />
54+
<Cell text="Date" css={[sTitle, sInfo]} />
5855
<Show when={showTime()}>
5956
<Cell text="Time" css={[sTitle, sInfo]} />
6057
</Show>
@@ -73,11 +70,15 @@ export const AttemptsTable: Component<AttemptsTableProps> = (props) => {
7370
<VerticalDivider />
7471
<Cell text="⏱️" css={[sTitle]} />
7572
<VerticalDivider />
76-
<Cell text="️🟡" css={[sTitle, sLastColumn]} />
73+
<Cell text="️🟡" css={[sTitle]} />
74+
<Show when={showAction()}>
75+
<VerticalDivider />
76+
<Cell text="⚙️" />
77+
</Show>
7778
<HorizontalDivider column={gridColumns()} thicknessFactor={GRID_SEPARATION_THICKNESS} />
7879
</Show>
7980

80-
<Cell row={attempt.rowSplits} text={attempt.date} css={[sFirstColumn, sInfo]} />
81+
<Cell row={attempt.rowSplits} text={attempt.date} css={[sInfo]} />
8182
<Show when={showTime()}>
8283
<Cell row={attempt.rowSplits} text={attempt.datetime} css={[sInfo]} />
8384
</Show>
@@ -119,14 +120,20 @@ export const AttemptsTable: Component<AttemptsTableProps> = (props) => {
119120
<VerticalDivider row={attempt.rowSplits} />
120121
<Switch>
121122
<Match when={!isDefined(attempt.time) && !isDefined(attempt.coins)}>
122-
<Cell column={GRID_RESULT_COLUMNS} row={attempt.rowSplits} css={[sLastColumn]} />
123+
<Cell column={GRID_RESULT_COLUMNS} row={attempt.rowSplits} />
123124
</Match>
124125
<Match when>
125126
<Cell row={attempt.rowSplits} text={attempt.time} css={[sValue]} />
126127
<VerticalDivider row={attempt.rowSplits} />
127-
<Cell row={attempt.rowSplits} text={attempt.coins} css={[sValue, sLastColumn]} />
128+
<Cell row={attempt.rowSplits} text={attempt.coins} css={[sValue]} />
128129
</Match>
129130
</Switch>
131+
<Show when={showAction()}>
132+
<VerticalDivider row={attempt.rowSplits} />
133+
<Cell row={attempt.rowSplits}>
134+
<SymbolButton symbol="🗑" onClick={() => props.onDelete?.(attempt)}></SymbolButton>
135+
</Cell>
136+
</Show>
130137
<HorizontalDivider column={GRID_SPLITS_COLUMNS} />
131138
</Show>
132139
</>
@@ -136,7 +143,13 @@ export const AttemptsTable: Component<AttemptsTableProps> = (props) => {
136143
<Match when={true}>
137144
<Cell column={GRID_SPLITS_COLUMNS} row={attempt.rowSplits} text="Not even one split 😭" />
138145
<VerticalDivider row={attempt.rowSplits} />
139-
<Cell row={attempt.rowSplits} column={GRID_RESULT_COLUMNS} css={[sLastColumn]} />
146+
<Cell row={attempt.rowSplits} column={GRID_RESULT_COLUMNS} css={[]} />
147+
<Show when={showAction()}>
148+
<VerticalDivider row={attempt.rowSplits} />
149+
<Cell row={attempt.rowSplits}>
150+
<SymbolButton symbol="🗑" onClick={() => props.onDelete?.(attempt)}></SymbolButton>
151+
</Cell>
152+
</Show>
140153
</Match>
141154
</Switch>
142155
<HorizontalDivider column={gridColumns()} thicknessFactor={GRID_SEPARATION_THICKNESS} />

src/domains/database/compositions/use-personal-repository.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as v from "valibot";
2-
import { entries, sum, values } from "remeda";
2+
import { entries, isDeepEqual, sum, values } from "remeda";
33
import type { AttemptEntity } from "../schemas/attempt-entity";
44
import type { AttemptsEntity } from "../schemas/attempts-entity";
55
import type { Brand } from "../../_core/utils/brand";
@@ -46,6 +46,14 @@ function usePersonalRepositorySingleton() {
4646
);
4747
};
4848

49+
const deleteAttempt = (delAttempt: AttemptEntity): void => {
50+
setStore(
51+
produce((store) => {
52+
store.attempts = store.attempts.filter((el) => !isDeepEqual(el, delAttempt));
53+
}),
54+
);
55+
};
56+
4957
const getAttemptsCountByTrack = (track: string): number => {
5058
return store.attemptsCountByTrack[track] ?? 0;
5159
};
@@ -82,6 +90,7 @@ function usePersonalRepositorySingleton() {
8290

8391
return {
8492
upsertAttempt,
93+
deleteAttempt,
8594
attempts,
8695
lastAttempt,
8796
getAttemptsCount,

src/domains/ui/components/dialog/dialog.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { type Component, type JSX, type Ref, Show } from "solid-js";
1+
import { type Component, type JSX, type Ref, Show, onMount } from "solid-js";
2+
import type { Classable } from "../../../_core/types/classable";
23
import { DialogContent } from "./dialog-content";
34
import { DialogContext } from "./dialog-context";
45
import { Portal } from "solid-js/web";
@@ -20,15 +21,22 @@ export interface DialogRef {
2021
close: () => void;
2122
}
2223

23-
interface DialogProps {
24+
interface DialogProps extends Classable {
2425
children: JSX.Element;
2526
title?: string;
2627
ref?: Ref<DialogRef>;
28+
onClose?: () => void;
2729
}
2830

2931
export const Dialog: Component<DialogProps> = (props) => {
3032
let dialog!: HTMLDialogElement; // oxlint-disable-line init-declarations no-unassigned-vars
3133

34+
onMount(() => {
35+
dialog?.addEventListener("close", () => {
36+
props.onClose?.();
37+
});
38+
});
39+
3240
const setDialog = (ref: HTMLDialogElement): void => {
3341
dialog = ref;
3442
if (isFunction(props.ref)) {
@@ -46,7 +54,7 @@ export const Dialog: Component<DialogProps> = (props) => {
4654

4755
return (
4856
<Portal>
49-
<dialog ref={setDialog} class={sDialog}>
57+
<dialog ref={setDialog} class={css(sDialog, props.class)}>
5058
<DialogContext.Provider value={dialog}>
5159
<Show when={props.title}>
5260
<h3>{props.title}</h3>

src/domains/ui/components/dialog/footers/dialog-footer-close.tsx

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,17 @@
1-
import { type Component, type Ref, useContext } from "solid-js";
1+
import { type Component, useContext } from "solid-js";
22
import { DialogContext } from "../dialog-context";
33
import { css } from "@emotion/css";
44

55
const sFooter = css({
66
display: "flex",
77
justifyContent: "flex-end",
88
gap: "var(--mk-spacing-medium)",
9-
"> button": {
10-
marginBottom: 0,
11-
},
129
});
1310

14-
interface DialogFooterCloseProps {
15-
onClose?: () => void;
16-
ref?: Ref<HTMLDialogElement>;
17-
}
18-
19-
export const DialogFooterClose: Component<DialogFooterCloseProps> = (props) => {
11+
export const DialogFooterClose: Component = () => {
2012
const dialog = useContext(DialogContext);
2113

2214
const onClose = (): void => {
23-
props.onClose?.();
2415
dialog?.close();
2516
};
2617

src/domains/ui/components/dialog/footers/dialog-footer-confirm.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ const sFooter = css({
66
display: "flex",
77
justifyContent: "flex-end",
88
gap: "var(--mk-spacing-medium)",
9-
"> button": {
10-
marginBottom: 0,
11-
},
129
});
1310

1411
interface DialogFooterConfirmProps {

src/domains/ui/components/grid/cell.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type Component, createMemo } from "solid-js";
1+
import { type Component, type JSX, createMemo } from "solid-js";
22
import type { CSSInterpolation } from "@emotion/css/create-instance";
33
import { css } from "@emotion/css";
44
import { isDefined } from "remeda";
@@ -9,6 +9,7 @@ export interface CellProps {
99
row?: number;
1010
column?: number;
1111
css?: CSSInterpolation;
12+
children?: JSX.Element;
1213
}
1314

1415
export const Cell: Component<CellProps> = (props) => {
@@ -25,5 +26,5 @@ export const Cell: Component<CellProps> = (props) => {
2526
return list;
2627
});
2728

28-
return <div class={css(cssList())}>{props.text ?? "-"}</div>;
29+
return <div class={css(cssList())}>{props.children ?? props.text ?? "-"}</div>;
2930
};

src/domains/ui/global-styles.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ export const injectGlobalStyles = (): void => {
5555
height: "auto",
5656
},
5757
dialog: {
58-
maxWidth: "80%",
58+
maxWidth: "80% !important",
59+
},
60+
button: {
61+
marginBottom: "0 !important",
5962
},
6063
});
6164
};

src/views/friends.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ const sActions = css({
1212
columnGap: "var(--mk-spacing-medium)",
1313
alignContent: "right",
1414
marginBottom: "var(--mk-spacing-large)",
15-
"> *": {
16-
marginBottom: 0,
17-
},
1815
});
1916

2017
export const Friends: Component = () => {

src/views/history.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { AttemptDeleteDialog, type AttemptDeleteDialogRef } from "../domains/attempt/components/attempt-delete-dialog";
12
import { type Component, type JSX, Show, onMount } from "solid-js";
23
import { ALL_TRACKS } from "../domains/attempt/constants";
34
import { AttemptsTable } from "../domains/attempt/components/attempts-table";
@@ -17,9 +18,6 @@ const sActions = css({
1718
alignContent: "center",
1819
alignItems: "center",
1920
marginBottom: "var(--mk-spacing-large)",
20-
"> *": {
21-
marginBottom: 0,
22-
},
2321
});
2422

2523
const sGrid = css({
@@ -36,6 +34,8 @@ const sHeader = css({
3634
});
3735

3836
export const History: Component = () => {
37+
let dialog!: AttemptDeleteDialogRef; // oxlint-disable-line init-declarations no-unassigned-vars
38+
3939
const personal = usePersonalRepository();
4040
const { setTitle } = usePageTitle();
4141
const { selectedTrack, filtered, AttemptsFilter } = useAttemptsFilter({
@@ -90,7 +90,18 @@ export const History: Component = () => {
9090
<AttemptsFilter />
9191
<OverallData />
9292
</div>
93-
<AttemptsTable attempts={filtered().attempts}></AttemptsTable>
93+
<AttemptsTable
94+
attempts={filtered().attempts}
95+
onDelete={(attempt) => {
96+
dialog.open(attempt);
97+
}}
98+
></AttemptsTable>
99+
<AttemptDeleteDialog
100+
ref={dialog}
101+
onDelete={(attempt) => {
102+
personal.deleteAttempt(attempt.raw);
103+
}}
104+
/>
94105
</>
95106
);
96107
};

0 commit comments

Comments
 (0)