Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0790cea
chore: convert alerts to solidjs component (@fehmer)
fehmer Mar 3, 2026
32f33f5
wip
fehmer Mar 3, 2026
737fb57
wip
Miodec Mar 5, 2026
4d1cd52
Merge branch 'master' into feature/solid-inbox
Miodec Mar 5, 2026
60460a0
wip
Miodec Mar 5, 2026
2eb7710
wip
Miodec Mar 5, 2026
4f5532d
show flex
Miodec Mar 5, 2026
fcf62f0
jsx
Miodec Mar 5, 2026
4bf612d
claiming
Miodec Mar 6, 2026
f70df2c
remove old code
Miodec Mar 6, 2026
9a14c80
refactor collection
fehmer Mar 7, 2026
a84e2b2
Merge branch 'master' into feature/solid-inbox
fehmer Mar 7, 2026
3b2727e
remove sync button, remove test code
fehmer Mar 7, 2026
67b0abd
Merge branch 'master' into feature/solid-inbox
fehmer Mar 7, 2026
678c905
chore: update claude.md
Miodec Mar 7, 2026
e40a22e
dev route to add inbox item
Miodec Mar 7, 2026
f01f38a
styling
Miodec Mar 7, 2026
54d7e51
style
Miodec Mar 7, 2026
d4a5114
only show delete all if all messages are claimed
fehmer Mar 7, 2026
056d204
move
Miodec Mar 7, 2026
5dcba27
recount unread
Miodec Mar 7, 2026
81bf142
handle read and unread
fehmer Mar 7, 2026
2d2d906
dont locally insert
Miodec Mar 7, 2026
57a7f4e
use effect to update unread size
Miodec Mar 7, 2026
4412e8d
update claude
Miodec Mar 7, 2026
40e0e2e
remove ts ignore
Miodec Mar 7, 2026
47b8a46
Update frontend/src/ts/components/popups/alerts/NotificationHistory.tsx
Miodec Mar 7, 2026
7082f1c
message
Miodec Mar 7, 2026
dfed132
throw if not 200
Miodec Mar 7, 2026
9fcb460
only claim unclaimed awards
fehmer Mar 8, 2026
75cd6d3
fix double claim
fehmer Mar 8, 2026
0406942
refetch inbox when adding items from dev modal
fehmer Mar 8, 2026
da42dab
collection error handling
fehmer Mar 8, 2026
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
2 changes: 1 addition & 1 deletion .claude/skills/review.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ Trigger: user asks to review changes, review code, or uses /review
- **Tailwind**: non-canonical classes, inline styles that should be Tailwind, missing responsive variants if siblings have them
- **Solid-specific**: broken reactivity, missing cleanup, doing things not the "Solid way"
- **Improvements**: any other changes that would make the code more robust, readable, maintainable, better
4. Output a concise list of findings. If nothing found, say "No issues found."
4. Output a concise list of findings. Highlight which issues should be absolutely fixed before merging/committing. If nothing found, say "No issues found."
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ Frontend is partially migrated from vanilla JS to SolidJS — new components use
Single test file: `pnpm vitest run path/to/test.ts`
For styling, use Tailwind CSS, class property, `cn` utility. Do not use classlist. Only colors available are those defined in Tailwind config.
In legacy code, use `i` tags with FontAwesome classes. In new code, use `Fa` component.
At the end of plan mode, give me a list of unresolved questions to answer, if any. Make them concise.
In plan mode, before writing up a plan, ask clarifying questions if needed. At the end of plan mode, give me a list of unresolved questions to answer, if any. Make them concise.
33 changes: 33 additions & 0 deletions backend/src/api/controllers/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import MonkeyError from "../../utils/error";

import { Mode, PersonalBest, PersonalBests } from "@monkeytype/schemas/shared";
import {
AddDebugInboxItemRequest,
GenerateDataRequest,
GenerateDataResponse,
} from "@monkeytype/contracts/dev";
import { buildMonkeyMail } from "../../utils/monkey-mail";
import { roundTo2 } from "@monkeytype/util/numbers";
import { MonkeyRequest } from "../types";
import { DBResult } from "../../utils/result";
Expand Down Expand Up @@ -42,6 +44,37 @@ export async function createTestData(
return new MonkeyResponse("test data created", { uid, email });
}

export async function addDebugInboxItem(
req: MonkeyRequest<undefined, AddDebugInboxItemRequest>,
): Promise<MonkeyResponse> {
const { uid } = req.ctx.decodedToken;
const { rewardType } = req.body;
const inboxConfig = req.ctx.configuration.users.inbox;

const rewards =
rewardType === "xp"
? [{ type: "xp" as const, item: 1000 }]
: rewardType === "badge"
? [{ type: "badge" as const, item: { id: 1 } }]
: [];

const body =
rewardType === "xp"
? "Here is your 1000 XP reward for debugging."
: rewardType === "badge"
? "Here is your Developer badge reward."
: "A debug inbox item with no reward.";

const mail = buildMonkeyMail({
subject: "Debug Inbox Item",
body,
rewards,
});

await UserDal.addToInbox(uid, [mail], inboxConfig);
return new MonkeyResponse("Debug inbox item added", null);
}

async function getOrCreateUser(
username: string,
password: string,
Expand Down
4 changes: 4 additions & 0 deletions backend/src/api/routes/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,8 @@ export default s.router(devContract, {
middleware: [onlyAvailableOnDev()],
handler: async (r) => callController(DevController.createTestData)(r),
},
addDebugInboxItem: {
middleware: [onlyAvailableOnDev()],
handler: async (r) => callController(DevController.addDebugInboxItem)(r),
},
});
14 changes: 3 additions & 11 deletions frontend/__tests__/components/common/AsyncContent.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,7 @@ describe("AsyncContent", () => {
}));

return (
<AsyncContent
query={myQuery}
errorMessage={options?.errorMessage}
alwaysShowContent={options?.alwaysShowContent}
ignoreError={options?.ignoreError}
loader={options?.loader}
>
<AsyncContent query={myQuery} {...(options as Props<string>)}>
{(data: string | undefined) => (
<>
foo
Expand Down Expand Up @@ -341,13 +335,11 @@ describe("AsyncContent", () => {
retry: 0,
}));

type Q = { first: string | undefined; second: string | undefined };
return (
<AsyncContent
queries={{ first: firstQuery, second: secondQuery }}
errorMessage={options?.errorMessage}
alwaysShowContent={options?.alwaysShowContent}
ignoreError={options?.ignoreError}
loader={options?.loader}
{...(options as Props<Q>)}
>
{(results: {
first: string | undefined;
Expand Down
3 changes: 3 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"@solid-primitives/refs": "1.1.2",
"@solid-primitives/transition-group": "1.1.2",
"@solidjs/meta": "0.29.4",
"@tanstack/pacer-lite": "0.2.1",
"@tanstack/query-db-collection": "1.0.27",
"@tanstack/solid-db": "0.2.10",
"@tanstack/solid-query": "5.90.23",
"@tanstack/solid-query-devtools": "5.91.3",
"@tanstack/solid-table": "8.21.3",
Expand Down
51 changes: 0 additions & 51 deletions frontend/src/html/popups.html
Original file line number Diff line number Diff line change
Expand Up @@ -51,57 +51,6 @@
</div>
</dialog>

<dialog id="alertsPopup" class="modalWrapper hidden">
<div class="modal">
<button class="mobileClose">
<i class="fas fa-times"></i>
Close
</button>
<div class="scrollWrapper">
<div class="accountAlerts">
<div class="title">
<div class="left">
<i class="fas fa-inbox"></i>
Inbox
</div>
<div class="right"></div>
</div>
<button class="claimAll hidden">
<i class="fas fa-fw fa-gift"></i>
Claim all
</button>
<button class="deleteAll hidden">
<i class="fas fa-fw fa-trash"></i>
Delete all
</button>
<div class="list">
<div class="nothing">Nothing to show</div>
</div>
</div>
<div class="separator accountSeparator"></div>
<div class="psas">
<div class="title">
<i class="fas fa-bullhorn"></i>
Announcements
</div>
<div class="list">
<div class="nothing">Nothing to show</div>
</div>
</div>
<div class="separator"></div>
<div class="notificationHistory">
<div class="title">
<i class="fas fa-comment-alt"></i>
Notifications
</div>
<div class="list">
<div class="nothing">Nothing to show</div>
</div>
</div>
</div>
</div>
</dialog>

<dialog id="simpleModal" class="modalWrapper hidden">
<form class="modal"></form>
</dialog>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
</div>
</div>
<mount data-component="modals" id="solidmodals"></mount>
<mount data-component="popups" id="solidpopups"></mount>
<div id="popups">
<load src="html/popups.html" />
</div>
Expand Down
4 changes: 1 addition & 3 deletions frontend/src/styles/media-queries-purple.scss
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,7 @@
border-radius: 0.3rem;
font-size: 0.6rem;
}
#alertsPopup .modal {
max-width: calc(100% - 4rem);
}

.popupWrapper .modal,
.modalWrapper .modal {
padding: 1rem;
Expand Down
183 changes: 0 additions & 183 deletions frontend/src/styles/popups.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1548,186 +1548,3 @@ body.darkMode {
}
}
}

#alertsPopup {
padding: 0;
justify-content: end;
z-index: 99999999;
overflow-x: hidden;
justify-items: end;
.modal {
background: var(--bg-color);
max-width: calc(350px + 2rem);
right: 0;
// height: calc(100vh - 4rem);
height: 100%;
top: 0;
padding: 2rem calc(1rem - 7px) 2rem 1rem; // -7px for the scrollbar
// padding: 1rem;
// border-radius: var(--roundness);
overflow: hidden;
margin-right: -10rem;
border-radius: var(--roundness) 0 0 var(--roundness);
display: block;

.mobileClose {
margin-bottom: 2rem;
width: 100%;
display: none;
}

.separator {
background-color: var(--sub-alt-color);
height: 0.25rem;
width: 100%;
border-radius: calc(var(--roundness) / 2);
}

.scrollWrapper {
padding: 0 1rem 0 1rem;
overflow-y: scroll;
display: grid;
gap: 2rem;
align-content: baseline;
height: 100%;
grid-auto-columns: 100%;
}
.accountAlerts > .title,
.notificationHistory > .title,
.psas > .title {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--sub-color);
-webkit-user-select: none;
user-select: none;
}
.accountAlerts > .claimAll,
.accountAlerts > .deleteAll {
font-size: 0.75em;
margin-bottom: 1rem;
width: 100%;
.fas {
margin-right: 0.25em;
}
}
.list {
display: grid;
gap: 1rem;
grid-template-columns: 100%;
.nothing {
width: 100%;
color: var(--text-color);
font-size: 0.75rem;
text-align: center;
margin: 2rem 0;
}
.preloader {
width: 100%;
color: var(--main-color);
text-align: center;
font-size: 1rem;
margin: 2rem 0;
}
.item {
display: grid;
grid-template-areas: "indicator title buttons" "indicator body buttons";
grid-template-columns: 0.25rem auto max-content;
gap: 0.25rem 0.5rem;
.indicator {
grid-area: indicator;
background-color: var(--sub-alt-color);
width: 0.25rem;
height: 100%;
border-radius: calc(var(--roundness) / 2);
transition: 0.125s;
&.main {
background-color: var(--main-color);
}
&.error {
background-color: var(--error-color);
}
&.sub {
background-color: var(--sub-color);
}
}
.title {
grid-area: title;
font-size: 0.75rem;
color: var(--sub-color);
}
.body {
grid-area: body;
font-size: 0.75rem;
color: var(--text-color);
transition: 0.125s;
opacity: 1;
word-wrap: break-word;
}
.buttons {
grid-area: buttons;
width: 100%;
display: grid;
grid-auto-flow: row;
gap: 0.5rem;
opacity: 0;
transition: 0.125s;
align-items: center;
align-content: center;
button {
font-size: 0.8em;
height: 100%;
display: grid;
}
}
&:hover,
&:focus-within {
.buttons {
opacity: 1;
}
.body {
opacity: 1;
}
}
}
}
.psas .list .item {
grid-template-areas: "indicator body";
grid-template-columns: 0.25rem calc(100% - 0.25rem);
.body {
opacity: 1;
}
}
.notificationHistory .list .item {
grid-template-areas: "indicator title buttons" "indicator body buttons";
.title {
font-size: 0.75rem;
color: var(--sub-color);
}
.body {
opacity: 1;
}
.highlight {
color: var(--main-color) !important;
}
}
.accountAlerts {
.title {
display: grid;
grid-template-columns: 1fr auto;
}
.list .item {
grid-template-areas: "indicator timestamp buttons" "indicator title buttons" "indicator body buttons";
.timestamp {
grid-area: timestamp;
font-size: 0.6rem;
color: var(--sub-color);
opacity: 0.5;
}
.rewards {
overflow: hidden;
margin-top: 0.35rem;
}
}
}
}
}
Loading