Skip to content

Commit 14cdcc1

Browse files
authored
Merge pull request #19 from hackclub/staging
Getting it ready for beta launch
2 parents 3d0a770 + bf4b81b commit 14cdcc1

File tree

9 files changed

+185
-144
lines changed

9 files changed

+185
-144
lines changed

src/app.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@
233233

234234
&:disabled,
235235
&.disabled {
236-
@apply cursor-not-allowed opacity-80 hover:outline-0;
236+
@apply cursor-not-allowed opacity-70 hover:outline-0;
237237
}
238238

239239
&.red {
@@ -257,6 +257,10 @@
257257
@apply border-3 border-primary-700 bg-primary-900 fill-primary-50 p-2 text-sm ring-primary-900 placeholder:text-primary-700 active:ring-3;
258258
}
259259

260+
.themed-input-on-box {
261+
@apply bg-primary-900 border-3 border-primary-800 ring-primary-700 focus:ring-2 transition-shadow rounded-lg placeholder:text-primary-700;
262+
}
263+
260264
.checkbox {
261265
@apply border-2 border-primary-600 bg-primary-900 ring-0 h-4 w-4 rounded-sm;
262266
}

src/routes/auth/callback/+server.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@ export async function GET(event) {
165165
return redirect(302, 'https://fraud.land');
166166
}
167167

168+
const isSuperAdmin =
169+
env.SUPER_ADMIN_SLACK_ID != undefined &&
170+
env.SUPER_ADMIN_SLACK_ID.length > 0 &&
171+
slack_id === env.SUPER_ADMIN_SLACK_ID;
172+
168173
if (databaseUser) {
169174
// Update user (update name and profile picture and lastLoginAt on login)
170175
await db
@@ -173,12 +178,11 @@ export async function GET(event) {
173178
name: username,
174179
profilePicture: profilePic,
175180
lastLoginAt: new Date(Date.now()),
176-
hackatimeTrust
181+
hackatimeTrust,
182+
hasAdmin: isSuperAdmin ? true : undefined
177183
})
178184
.where(eq(user.idvId, id));
179185
} else {
180-
const isSuperAdmin = slack_id === env.SUPER_ADMIN_SLACK_ID;
181-
182186
// Create user
183187
await db.insert(user).values({
184188
idvId: id,

src/routes/dashboard/Sidebar.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
<p class="truncate font-medium">
6161
{user.name}
6262
</p>
63-
<p class="text-sm">{user.clay} clay · {user.brick} brick</p>
63+
<p class="text-sm">{Math.floor(user.clay)} clay · {Math.floor(user.brick)} brick</p>
6464
</div>
6565
</a>
6666
<SidebarButton icon={LogOut} href="/auth/logout">Log out</SidebarButton>

src/routes/dashboard/admin/admin/+page.svelte

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script lang="ts">
22
import Head from '$lib/components/Head.svelte';
3+
import { Users } from '@lucide/svelte';
34
45
let { data } = $props();
56
</script>
@@ -9,5 +10,15 @@
910
<div class="flex h-full flex-col">
1011
<h1 class="mt-5 mb-3 font-hero text-3xl font-medium">Admin</h1>
1112

12-
<a href="/dashboard/admin/admin/users" class="underline">Users</a>
13+
<div class="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6">
14+
<a
15+
class="themed-box flex flex-col items-center justify-center gap-2 p-3 shadow-xl transition-transform hover:scale-105"
16+
href="/dashboard/admin/admin/users"
17+
>
18+
<div>
19+
<Users size={40} />
20+
</div>
21+
<p class="text-2xl font-bold">Users</p>
22+
</a>
23+
</div>
1324
</div>

src/routes/dashboard/admin/admin/users/+page.svelte

Lines changed: 18 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
1010
let userSearch = $state('');
1111
12-
let users = $derived(data.users); //form?.users ??
12+
let users = $derived(data.users); //form?.users ??
1313
1414
let filteredProjects = $derived(
15-
data.users.filter((user) =>
16-
user.name?.toLowerCase().includes(userSearch.toLowerCase())
17-
)
15+
data.users.filter((user) => user.name?.toLowerCase().includes(userSearch.toLowerCase()))
1816
);
1917
let filteredUsers = $derived(
2018
data.users.filter((user) => user.name.toLowerCase().includes(userSearch.toLowerCase()))
@@ -28,92 +26,6 @@
2826
<div class="flex h-full flex-col">
2927
<h1 class="mt-5 mb-3 font-hero text-3xl font-medium">Users</h1>
3028

31-
<!--<div class="flex flex-col-reverse gap-5 lg:flex-row">
32-
<div class="themed-box grow p-3">
33-
<h2 class="mb-2 text-xl font-bold">Filter & Sort</h2>
34-
<form
35-
method="POST"
36-
use:enhance={() => {
37-
formPending = true;
38-
return async ({ update }) => {
39-
await update();
40-
formPending = false;
41-
};
42-
}}
43-
>
44-
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 md:grid-cols-3">
45-
<!-- Project status --/>
46-
<label class="flex flex-col gap-1">
47-
<span class="font-medium">Status</span>
48-
<select
49-
class="h-40 grow border-3 border-primary-700 bg-primary-900 fill-primary-50 p-2 text-sm ring-primary-900 placeholder:text-primary-900 active:ring-3"
50-
name="status"
51-
value={form?.fields.status ?? ['submitted']}
52-
multiple
53-
>
54-
<option value="building" class="truncate">Building</option>
55-
<option value="submitted" class="truncate">Submitted</option>
56-
<option value="t1_approved" class="truncate">Review approved</option>
57-
<option value="t2_approved" class="truncate">YSWS review approved</option>
58-
<option value="finalized" class="truncate">Finalized</option>
59-
<option value="rejected" class="truncate">Rejected</option>
60-
<option value="rejected_locked" class="truncate">Rejected (locked)</option>
61-
</select>
62-
</label>
63-
64-
<!-- Project --/>
65-
<label class="flex flex-col">
66-
<span class="mb-1 font-medium">Project</span>
67-
<div class="flex h-40 flex-col">
68-
<input
69-
type="text"
70-
placeholder="search"
71-
bind:value={projectSearch}
72-
class="themed-input-light border-b-0 py-1.5"
73-
/>
74-
<select
75-
class="themed-input-light grow"
76-
name="project"
77-
value={form?.fields.project ?? []}
78-
multiple
79-
>
80-
{#each filteredProjects as project}
81-
<option value={project.id} class="truncate">{project.name}</option>
82-
{/each}
83-
</select>
84-
</div>
85-
</label>
86-
87-
<!-- User --/>
88-
<label class="flex flex-col">
89-
<span class="mb-1 font-medium">User</span>
90-
<div class="flex h-40 flex-col">
91-
<input
92-
type="text"
93-
placeholder="search"
94-
bind:value={userSearch}
95-
class="themed-input-light border-b-0 py-1.5"
96-
/>
97-
<select
98-
class="themed-input-light grow"
99-
name="user"
100-
value={form?.fields.user ?? []}
101-
multiple
102-
>
103-
{#each filteredUsers as user}
104-
<option value={user?.id} class="truncate">{user?.name}</option>
105-
{/each}
106-
</select>
107-
</div>
108-
</label>
109-
</div>
110-
<button type="submit" class="button md primary mt-3 w-full" disabled={formPending}>Apply!</button>
111-
</form>
112-
</div>
113-
</div>
114-
115-
<h2 class="mt-4 mb-2 text-2xl font-bold">Filtered users</h2>-->
116-
11729
{#if users.length == 0}
11830
<div class="flex grow items-center justify-center">
11931
<div>
@@ -135,44 +47,29 @@
13547
<a
13648
class="absolute inset-0 z-1"
13749
href={`/dashboard/admin/admin/users/${user.id}`}
138-
aria-label="project"
50+
aria-label="user"
13951
>
14052
</a>
14153
<h1 class="flex flex-row gap-1 text-xl font-semibold">
14254
<span class="grow truncate">{user.name}</span>
14355
</h1>
144-
<!-- <p class="text-sm">
145-
by <a class="relative z-2 underline" href={`/dashboard/users/${project.user?.id}`}
146-
>{project.user?.name}</a
56+
<p>
57+
<span class="rounded-sm bg-primary-800 px-1 text-nowrap"
58+
>{user.hasT1Review ? 't1 review' : '-'}</span
59+
>
60+
<span class="rounded-sm bg-primary-700 px-1 text-nowrap"
61+
>{user.hasT2Review ? 't2 review' : '-'}</span
62+
>
63+
<span class="rounded-sm bg-primary-600 px-1 text-nowrap"
64+
>{user.hasAdmin ? 'admin' : '-'}</span
14765
>
14866
</p>
149-
<p class="grow">{project.project.description}</p>
150-
{#if project.project.url && project.project.url.length > 0}
151-
<div class="my-2 flex">
152-
<a class="button sm primary relative z-2" href={project.project.url} target="_blank">
153-
<ExternalLink />
154-
Link to project
155-
</a>
156-
</div>
157-
{:else}
158-
<div class="mb-2"></div>
159-
{/if}
160-
<p class="text-sm">
161-
{project.devlogCount} journal{project.devlogCount !== 1 ? 's' : ''} ∙ {Math.floor(
162-
project.timeSpent / 60
163-
)}h {project.timeSpent % 60}min
164-
</p>
165-
<div class="flex flex-row gap-4">
166-
<p class="grow text-sm">
167-
Created <abbr
168-
title={`${project.project.createdAt.toUTCString()}`}
169-
class="relative z-2"
170-
>
171-
{relativeDate(project.project.createdAt)}
172-
</abbr>
173-
</p>
174-
<p class="text-sm">{projectStatuses[project.project.status]}</p>
175-
</div> -->
67+
<code>
68+
{user.slackId}
69+
</code>
70+
<p>Hackatime: {user.hackatimeTrust}</p>
71+
<p>Trust: {user.trust}</p>
72+
<p>{user.clay} clay, {user.brick} brick, {user.shopScore} market</p>
17673
</div>
17774
{/each}
17875
</div>

src/routes/dashboard/admin/admin/users/[id]/+page.server.ts

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { db } from '$lib/server/db/index.js';
22
import { user, devlog, session } from '$lib/server/db/schema.js';
3-
import { error } from '@sveltejs/kit';
4-
import { eq, sql } from 'drizzle-orm';
3+
import { error, fail } from '@sveltejs/kit';
4+
import { and, eq, sql } from 'drizzle-orm';
55
import type { Actions } from './$types';
66

77
export async function load({ locals, params }) {
@@ -20,7 +20,7 @@ export async function load({ locals, params }) {
2020
devlogCount: sql`COALESCE(COUNT(${devlog.id}), 0)`
2121
})
2222
.from(user)
23-
.leftJoin(devlog, eq(devlog.userId, user.id))
23+
.leftJoin(devlog, and(eq(devlog.userId, user.id), eq(devlog.deleted, false)))
2424
.where(eq(user.id, id))
2525
.groupBy(user.id)) ?? [{ devlogCount: 0 }];
2626

@@ -51,8 +51,6 @@ export const actions = {
5151
const hasAdmin = data.get('has_admin');
5252
const hasProjectAuditLogs = data.get('has_project_audit_logs');
5353

54-
// TODO: add check to disable un-admining superadmin
55-
5654
await db
5755
.update(user)
5856
.set({
@@ -74,6 +72,57 @@ export const actions = {
7472
};
7573
},
7674

75+
currency: async ({ locals, request, params }) => {
76+
if (!locals.user) {
77+
throw error(500);
78+
}
79+
if (!locals.user.hasAdmin) {
80+
throw error(403, { message: 'get out, peasant' });
81+
}
82+
83+
const id: number = parseInt(params.id);
84+
85+
const data = await request.formData();
86+
const clay = data.get('clay');
87+
const brick = data.get('brick');
88+
const shopScore = data.get('market_score');
89+
90+
if (
91+
!clay ||
92+
isNaN(parseFloat(clay.toString())) ||
93+
!brick ||
94+
isNaN(parseFloat(brick.toString())) ||
95+
!shopScore ||
96+
isNaN(parseFloat(shopScore.toString()))
97+
) {
98+
return fail(400, {
99+
currency: {
100+
fields: { clay, brick, shopScore },
101+
invalidFields: true
102+
}
103+
});
104+
}
105+
106+
await db
107+
.update(user)
108+
.set({
109+
clay: parseFloat(clay.toString()),
110+
brick: parseFloat(brick.toString()),
111+
shopScore: parseFloat(shopScore.toString())
112+
})
113+
.where(eq(user.id, id));
114+
115+
const [queriedUser] = await db.select().from(user).where(eq(user.id, id));
116+
117+
if (!queriedUser) {
118+
throw error(404, { message: 'user not found' });
119+
}
120+
121+
return {
122+
queriedUser
123+
};
124+
},
125+
77126
refreshHackatime: async ({ locals, params }) => {
78127
if (!locals.user) {
79128
throw error(500);

0 commit comments

Comments
 (0)