Skip to content

Commit 5aa7b00

Browse files
authored
Remade the activity tracker (#174)
* Remade the activity tracker
1 parent 3c8f4b5 commit 5aa7b00

11 files changed

Lines changed: 1406 additions & 818 deletions

File tree

Orbit-activity.rbxmx

Lines changed: 971 additions & 711 deletions
Large diffs are not rendered by default.

components/settings/general/activity.tsx

Lines changed: 100 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,44 @@ function SettingField({
5151
)
5252
}
5353

54+
function ToggleRow({
55+
title,
56+
hint,
57+
checked,
58+
disabled,
59+
onChange,
60+
badge,
61+
}: {
62+
title: string
63+
hint: string
64+
checked: boolean
65+
disabled?: boolean
66+
onChange?: () => void
67+
badge?: string
68+
}) {
69+
return (
70+
<div className="flex flex-row items-center justify-between gap-4 py-3.5">
71+
<div className="min-w-0">
72+
<p className="flex items-center gap-1.5 text-sm font-medium text-zinc-800 dark:text-zinc-200">
73+
{title}
74+
{badge ? (
75+
<span className="inline-flex items-center whitespace-nowrap rounded-full bg-blue-500/10 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-blue-600 dark:text-blue-400">
76+
{badge}
77+
</span>
78+
) : null}
79+
</p>
80+
<p className="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5">{hint}</p>
81+
</div>
82+
<div className="flex shrink-0 items-center gap-2">
83+
<span className="w-8 text-right text-xs tabular-nums text-zinc-500 dark:text-zinc-400">
84+
{disabled ? "Off" : checked ? "On" : "Off"}
85+
</span>
86+
<SwitchComponenet checked={checked} disabled={disabled} onChange={onChange} label="" />
87+
</div>
88+
</div>
89+
)
90+
}
91+
5492
type props = {
5593
triggerToast: typeof toast;
5694
hasResetActivityOnly?: boolean;
@@ -67,11 +105,10 @@ const Activity: FC<props> = (props) => {
67105
const [isResetDialogOpen, setIsResetDialogOpen] = useState(false);
68106
const [isResetting, setIsResetting] = useState(false);
69107
const [leaderboardEnabled, setLeaderboardEnabled] = useState(false);
70-
const [idleTimeEnabled, setIdleTimeEnabled] = useState(true);
71-
const [leaderboardStyle, setLeaderboardStyle] = useState<"list" | "podium">(
72-
"list"
73-
);
108+
const [leaderboardStyle, setLeaderboardStyle] = useState<"list" | "podium">("list");
74109
const [scheduleEnabled, setScheduleEnabled] = useState(false);
110+
const [privateServerEnabled, setPrivateServerEnabled] = useState(false);
111+
const [studioEnabled, setStudioEnabled] = useState(false);
75112
const [scheduleDay, setScheduleDay] = useState<string>("monday");
76113
const [scheduleFrequency, setScheduleFrequency] = useState<string>("weekly");
77114
const router = useRouter();
@@ -85,7 +122,8 @@ const Activity: FC<props> = (props) => {
85122
setRoles(res.data.roles);
86123
setSelectedRole(res.data.currentRole);
87124
setSelectedLRole(res.data.leaderboardRole);
88-
setIdleTimeEnabled(res.data.idleTimeEnabled ?? true);
125+
setPrivateServerEnabled(res.data.privateServerEnabled);
126+
setStudioEnabled(res.data.studioEnabled);
89127
}
90128
})();
91129
}, []);
@@ -204,18 +242,38 @@ const Activity: FC<props> = (props) => {
204242
}
205243
};
206244

207-
const updateIdleTimeEnabled = async (enabled: boolean) => {
245+
const updatePrivateServer = async (enabled: boolean) => {
246+
const previous = privateServerEnabled;
247+
setPrivateServerEnabled(enabled);
208248
try {
209249
const req = await axios.post(
210-
`/api/workspace/${workspace.groupId}/settings/activity/setIdleTime`,
250+
`/api/workspace/${workspace.groupId}/settings/activity/setprivateserver`,
211251
{ enabled: enabled }
212252
);
213253
if (req.status === 200) {
214-
setIdleTimeEnabled(enabled);
215-
triggerToast.success("Updated idle time tracking!");
254+
setPrivateServerEnabled(req.data.enabled);
255+
triggerToast.success("Updated Private Server tracking!");
216256
}
217257
} catch (error: any) {
218-
triggerToast.error("Failed to update idle time tracking.");
258+
setPrivateServerEnabled(previous);
259+
triggerToast.error("Failed to update private server tracking.");
260+
}
261+
};
262+
const updateStudio = async (enabled: boolean) => {
263+
const previous = studioEnabled;
264+
setStudioEnabled(enabled);
265+
try {
266+
const req = await axios.post(
267+
`/api/workspace/${workspace.groupId}/settings/activity/setstudio`,
268+
{ enabled: enabled }
269+
);
270+
if (req.status === 200) {
271+
setStudioEnabled(req.data.enabled);
272+
triggerToast.success("Updated Studio tracking!");
273+
}
274+
} catch (error: any) {
275+
setStudioEnabled(previous);
276+
triggerToast.error("Failed to update studio tracking.");
219277
}
220278
};
221279

@@ -295,7 +353,7 @@ const Activity: FC<props> = (props) => {
295353
<div className="min-w-0">
296354
<p className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">Activity</p>
297355
<p className="text-xs text-zinc-400 dark:text-zinc-500 mt-0.5">
298-
Who is tracked, idle detection, and the desktop loader for in-game time.
356+
Who is tracked, settings, and the desktop loader for in-game time.
299357
</p>
300358
</div>
301359
</div>
@@ -308,15 +366,15 @@ const Activity: FC<props> = (props) => {
308366
>
309367
<Listbox value={selectedRole} onChange={(value: number) => updateRole(value)} as="div" className="relative">
310368
<Listbox.Button className={listboxButtonClass}>
311-
<span className="truncate">{(roles.find((r: any) => r.rank === selectedRole) as any)?.name || "Select a role"}</span>
369+
<span className="truncate">{(roles.find((r: any) => r.rank === selectedRole) as any)?.name || 'Select a role'}</span>
312370
<IconChevronDown className="w-4 h-4 text-zinc-400 shrink-0" />
313371
</Listbox.Button>
314372
<Listbox.Options className={listboxOptionsClass}>
315373
{roles.filter((role: any) => role.rank > 0).map((role: any, index) => (
316374
<Listbox.Option key={index} value={role.rank} className={({ active }) => listboxOptionClass(active)}>
317375
{({ selected }) => (
318376
<>
319-
<span className={`${selected ? "font-semibold" : ""} flex gap-2` }>{role.name} <span className="text-zinc-400 dark:text-zinc-500">(Group rank: {role.rank})</span> </span>
377+
<span className={`${selected ? "font-semibold" : ""} flex gap-2`}>{role.name} <span className="text-zinc-400 dark:text-zinc-500">(Group rank: {role.rank})</span> </span>
320378
{selected && <IconCheck className="w-4 h-4 text-[color:rgb(var(--group-theme))]" />}
321379
</>
322380
)}
@@ -326,25 +384,37 @@ const Activity: FC<props> = (props) => {
326384
</Listbox>
327385
</SettingField>
328386
</div>
329-
<div className="pt-5 pb-5">
330-
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
331-
<div className="min-w-0">
332-
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-200">Idle time tracking</p>
333-
<p className="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5">Count time when staff are away from keyboard.</p>
334-
</div>
335-
<div className="flex items-center justify-end gap-2 shrink-0">
336-
<span className="text-xs text-zinc-500 dark:text-zinc-400 tabular-nums w-12 text-right">{idleTimeEnabled ? "On" : "Off"}</span>
337-
<SwitchComponenet checked={idleTimeEnabled} onChange={() => updateIdleTimeEnabled(!idleTimeEnabled)} label="" />
338-
</div>
339-
</div>
387+
388+
<div className="divide-y divide-zinc-100 dark:divide-zinc-800/80">
389+
<ToggleRow
390+
title="Private Servers"
391+
hint="Enable if you want the tracker to track the activity of players in a private server."
392+
checked={privateServerEnabled}
393+
onChange={() => updatePrivateServer(!privateServerEnabled)}
394+
/>
395+
<ToggleRow
396+
title="Studio Sessions"
397+
hint="Enable if you want the tracker to track the activity of players on a studio session."
398+
checked={studioEnabled}
399+
onChange={() => updateStudio(!studioEnabled)}
400+
/>
401+
<ToggleRow
402+
title="Workspace Bans"
403+
hint="Enable it if you don't want the tracker to track staff who are banned from the workspace."
404+
checked={false}
405+
disabled
406+
badge="Coming soon"
407+
/>
340408
</div>
409+
341410
<div className="pt-5">
342411
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-200">Activity loader</p>
343412
<p className="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5 mb-3">Install this on your machine to report session time to the workspace.</p>
344413
<button
345414
type="button"
346415
onClick={downloadLoader}
347-
className="inline-flex w-full sm:w-auto items-center justify-center gap-2 rounded-xl bg-[color:rgb(var(--group-theme))] px-4 py-2.5 text-sm font-medium text-white shadow-sm transition hover:opacity-90"
416+
disabled={!selectedRole}
417+
className="inline-flex w-full sm:w-auto items-center justify-center gap-2 rounded-xl disabled:bg-white/40 disabled:hover:bg-white/40 disabled:cursor-not-allowed bg-[color:rgb(var(--group-theme))] px-4 py-2.5 text-sm font-medium text-white shadow-sm transition-all hover:opacity-90"
348418
>
349419
<IconDownload className="h-4 w-4" stroke={1.5} />
350420
Download loader
@@ -506,13 +576,13 @@ const Activity: FC<props> = (props) => {
506576
)}
507577

508578
<div className="space-y-4 rounded-2xl bg-zinc-50 dark:bg-zinc-800/50 p-4">
509-
<div className="flex flex-col gap-3 sm:min-h-[2.5rem] sm:flex-row sm:items-center sm:justify-between sm:gap-4">
510-
<div className="min-w-0 pr-0 sm:pr-2">
579+
<div className="flex flex-row items-center justify-between gap-4">
580+
<div className="min-w-0">
511581
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-200">Automatic reset</p>
512582
<p className="mt-0.5 text-xs leading-relaxed text-zinc-500 dark:text-zinc-400">Run a reset on a recurring schedule (saved separately from manual reset).</p>
513583
</div>
514-
<div className="flex shrink-0 items-center justify-end gap-2 sm:pt-0">
515-
<span className="w-8 text-right text-xs text-zinc-500 tabular-nums">{scheduleEnabled ? "On" : "Off"}</span>
584+
<div className="flex shrink-0 items-center gap-2">
585+
<span className="w-8 text-right text-xs tabular-nums text-zinc-500 dark:text-zinc-400">{scheduleEnabled ? "On" : "Off"}</span>
516586
<SwitchComponenet
517587
checked={scheduleEnabled}
518588
onChange={() => setScheduleEnabled(!scheduleEnabled)}
@@ -679,4 +749,4 @@ const Activity: FC<props> = (props) => {
679749
Activity.title = "Activity";
680750
Activity.isAboveOthers = true;
681751

682-
export default Activity;
752+
export default Activity;

components/switch.tsx

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,47 @@
1-
import React, { FC, ReactNode } from "react";
2-
import { twMerge } from "tailwind-merge";
3-
import { workspacestate } from "@/state";
4-
import { useRecoilState } from "recoil";
5-
import { Switch } from "@headlessui/react";
6-
type Props = {
7-
onChange?: () => void;
8-
label: string;
9-
classoverride?: string;
10-
disabled?: boolean | false;
11-
checked?: boolean | false;
1+
import { FC } from "react";
2+
import clsx from "clsx";
123

4+
type Props = {
5+
onChange?: () => void;
6+
label: string;
7+
classoverride?: string;
8+
disabled?: boolean;
9+
checked?: boolean;
1310
};
1411

15-
const SwitchComponenet: FC<Props> = ({ disabled, onChange, label, checked }: Props) => {
16-
return (
17-
<div className="flex flex-row">
18-
<Switch
19-
checked={checked}
20-
className={`${checked ? 'bg-primary' : 'bg-zinc-200'
21-
} relative inline-flex h-6 w-11 items-center rounded-full shadow-inner mb-2`}
22-
onChange={onChange}
23-
>
24-
<span
25-
className={`${checked ? 'translate-x-6' : 'translate-x-1'
26-
} inline-block h-4 w-4 transform rounded-full bg-white transition shadow`}
27-
/>
28-
</Switch>
29-
<p className="ml-2 dark:text-white">{label}</p>
30-
</div>
31-
32-
);
12+
const SwitchComponenet: FC<Props> = ({
13+
disabled,
14+
onChange,
15+
label,
16+
checked,
17+
classoverride,
18+
}: Props) => {
19+
return (
20+
<div className={clsx("flex items-center", classoverride)}>
21+
<button
22+
type="button"
23+
role="switch"
24+
aria-checked={checked}
25+
disabled={disabled}
26+
onClick={onChange}
27+
className={clsx(
28+
"relative inline-flex h-6 w-11 shrink-0 rounded-full transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2",
29+
disabled
30+
? "cursor-not-allowed bg-zinc-200 dark:bg-zinc-700"
31+
: "cursor-pointer",
32+
!disabled && (checked ? "bg-primary" : "bg-zinc-200 dark:bg-zinc-700")
33+
)}
34+
>
35+
<span
36+
className="inline-block h-5 w-5 rounded-full bg-white shadow-sm transition-transform duration-200 mt-0.5"
37+
style={{ transform: checked ? "translateX(22px)" : "translateX(2px)" }}
38+
/>
39+
</button>
40+
{label ? (
41+
<p className="ml-2 text-sm text-zinc-700 dark:text-white">{label}</p>
42+
) : null}
43+
</div>
44+
);
3345
};
3446

35-
export default SwitchComponenet;
47+
export default SwitchComponenet;

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)