Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Raidy is a raid scheduling project for Discord statics: the web console is where everyone plans, and Discord carries the announcements. One Go process runs the Discord bot, web API, scheduler, and production web assets, backed by PostgreSQL.

**Status:** `v0.0.16` is an early development snapshot.
**Status:** `v0.0.17` is an early development snapshot.

```text
backend/ Go service, Discord interactions, web API, PostgreSQL migrations
Expand Down
1 change: 1 addition & 0 deletions web/.prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ package-lock.json
src/components/ui
test-results
playwright-report
dist-demo
4 changes: 1 addition & 3 deletions web/e2e/web.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,6 @@ test("schedule edits show an unsaved-changes bar that can be discarded", async (
await mockSignedInLeader(page);
await page.goto("/app");
await page.getByRole("button", { name: "Schedule" }).click();
// Saving a new time moves the open period too; the page must say so.
await expect(page.getByText(/moves upcoming dates in open periods/)).toBeVisible();
await expect(page.getByText("You have unsaved changes")).toHaveCount(0);
await page
.getByRole("radio", { name: "Mon" })
Expand Down Expand Up @@ -85,7 +83,7 @@ test("schedule shows the saved destination and loads channels only on demand", a
await page.goto("/app");
await page.getByRole("button", { name: "Schedule" }).click();
// Regression: saved names used to render as "Discord server" / "Saved channel".
await expect(page.getByText("Posts go to Scions.")).toBeVisible();
await expect(page.getByText("in Scions")).toBeVisible();
await expect(page.getByText("#raid-schedule")).toBeVisible();
await expect(page.getByText("Saved channel")).toHaveCount(0);
expect(recorder.requests.some((request) => request.includes("/channels"))).toBe(false);
Expand Down
4 changes: 2 additions & 2 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion web/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "raidy-web",
"private": true,
"version": "0.0.16",
"version": "0.0.17",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
5 changes: 1 addition & 4 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,7 @@ function NoServers({ inviteUrl }: { inviteUrl: string }) {
return (
<div className="max-w-lg">
<h1 className="text-xl font-semibold tracking-tight">Raidy isn't in any of your servers</h1>
<p className="mt-2 text-sm text-muted-foreground">
Teams belong to a Discord server, and Raidy needs to be installed there before you can pick teammates or post
timetables. Add it to a server you manage, then reload this page.
</p>
<p className="mt-2 text-sm text-muted-foreground">Add it to a server you manage, then reload.</p>
<div className="mt-6 flex gap-2">
{inviteUrl && (
<Button asChild>
Expand Down
20 changes: 5 additions & 15 deletions web/src/features/overview/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,8 @@ export function Overview({
if (!schedule) {
return (
<div className="max-w-lg">
<h1 className="text-xl font-semibold tracking-tight">No timetable yet</h1>
<p className="mt-2 text-sm text-muted-foreground">
{team.isLeader
? "Choose the days and time your static raids. Raidy then opens voting and announces the dates in Discord before every period."
: "Your leader hasn't set up the recurring schedule yet."}
</p>
<h1 className="text-xl font-semibold tracking-tight">No schedule yet</h1>
{!team.isLeader && <p className="mt-2 text-sm text-muted-foreground">Your leader hasn't set one up.</p>}
{team.isLeader && (
<Button className="mt-6" onClick={onEditSchedule}>
Set up schedule
Expand All @@ -130,8 +126,8 @@ export function Overview({

const summary = `${describeSchedule(schedule)} · ${schedule.timezone}`;
const nextPost = schedule.enabled
? `Next timetable posts ${formatDateTime(schedule.nextPublishAt, browserTimezone())}`
: "Automatic posting is paused";
? `Next post ${formatDateTime(schedule.nextPublishAt, browserTimezone())}`
: "Posting paused";

if (!poll) {
return (
Expand Down Expand Up @@ -177,10 +173,7 @@ export function Overview({
}
/>
{canVote && (
<div className="mb-3 flex flex-wrap items-center justify-between gap-3">
<p className="text-sm text-muted-foreground">
{draft ? "Tap each date you can make, then save." : "Your answers are in the first row."}
</p>
<div className="mb-3 flex justify-end gap-3">
{draft ? (
<div className="flex gap-2">
<Button variant="ghost" size="sm" disabled={savingVotes} onClick={() => setDraft(null)}>
Expand All @@ -207,9 +200,6 @@ export function Overview({
draft={draft}
onToggle={toggle}
/>
{team.isLeader && !draft && (
<p className="mt-3 text-xs text-muted-foreground">Use ⋯ under a date to confirm, cancel, or reopen it.</p>
)}
</>
);
}
27 changes: 10 additions & 17 deletions web/src/features/schedule/schedule-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,7 @@ export function SchedulePanel({
}
}

if (!team.isLeader)
return <PageHeader title="Schedule" detail="Only the team leader can change the recurring schedule." />;
if (!team.isLeader) return <PageHeader title="Schedule" detail="Only the leader can change it." />;
if (loading) return <LoadingPanel />;

const dirty = !saved || JSON.stringify(schedulePayload(form)) !== JSON.stringify(schedulePayload(saved));
Expand All @@ -198,8 +197,8 @@ export function SchedulePanel({
title="Schedule"
detail={
saved
? `${describeSchedule(saved)} · ${saved.enabled ? "posting automatically" : "automatic posting paused"}`
: "Set the raid days once. Raidy opens voting and announces the dates in Discord before every period."
? `${describeSchedule(saved)} · ${saved.enabled ? "posting automatically" : "posting paused"}`
: undefined
}
actions={
saved && (
Expand All @@ -219,14 +218,7 @@ export function SchedulePanel({
/>
<form className="grid gap-x-12 lg:grid-cols-[minmax(0,1fr)_300px]" onSubmit={submit}>
<div>
<Section
title="When"
description={
saved
? "A new time or timezone also moves upcoming dates in open periods and keeps their votes. Day changes start with the next period."
: "One time slot, repeated on the days you pick."
}
>
<Section title="When">
<fieldset>
<Label asChild>
<legend>Raid days</legend>
Expand Down Expand Up @@ -267,7 +259,7 @@ export function SchedulePanel({
</Field>
</Section>

<Section title="Voting" description="Each period opens its own availability vote, announced in Discord.">
<Section title="Voting">
<div className="grid gap-4 sm:grid-cols-2">
<Field label="Repeats">
<Select
Expand Down Expand Up @@ -306,7 +298,7 @@ export function SchedulePanel({
</Field>
</Section>

<Section title="Discord" description={`Posts go to ${team.guildName || "the team's server"}.`}>
<Section title="Discord">
{choosingChannel ? (
<Field label="Channel">
{channelsLoading ? (
Expand Down Expand Up @@ -341,7 +333,7 @@ export function SchedulePanel({
!channelsLoading &&
channels.length === 0 && (
<p className="mt-2 text-sm text-muted-foreground">
No channel allows Raidy to post. It needs View Channel, Send Messages, and Embed Links.
Raidy can't post in any channel. It needs View Channel, Send Messages and Embed Links.
</p>
)
)}
Expand All @@ -356,6 +348,7 @@ export function SchedulePanel({
<div className="text-sm">
<span className="text-muted-foreground">Channel </span>
<span className="font-medium">{form.channelName ? `#${form.channelName}` : "Saved channel"}</span>
{team.guildName && <span className="text-muted-foreground"> in {team.guildName}</span>}
</div>
<Button type="button" variant="outline" size="sm" onClick={() => setChoosingChannel(true)}>
Change
Expand All @@ -369,7 +362,7 @@ export function SchedulePanel({

{dirty && (
<div className="sticky bottom-4 z-20 mt-6 flex items-center justify-between gap-4 rounded-md border bg-popover px-4 py-3 shadow-sm lg:col-span-2">
<span className="text-sm">{saved ? "You have unsaved changes" : "Review the preview, then activate"}</span>
<span className="text-sm">{saved ? "You have unsaved changes" : "Not active yet"}</span>
<div className="flex gap-2">
{saved && (
<Button type="button" variant="ghost" size="sm" onClick={() => setForm(saved)}>
Expand All @@ -393,7 +386,7 @@ export function SchedulePreview({ schedule, error }: { schedule: Schedule | null
<h2 className="text-sm font-medium">First period</h2>
{!schedule ? (
<p className={`mt-3 text-sm ${error ? "text-destructive" : "text-muted-foreground"}`}>
{error || "Pick at least one day and a channel to preview dates."}
{error || "Pick days and a channel."}
</p>
) : (
<>
Expand Down
17 changes: 5 additions & 12 deletions web/src/features/team/team-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export function TeamPanel({
if (team && !team.isLeader) {
return (
<>
<PageHeader title="Roster" detail="Only the team leader can change the roster." />
<PageHeader title="Roster" />
<RosterList
members={team.members.map((member) => ({ member, role: member.id === team.leaderId ? "Leader" : "Member" }))}
/>
Expand All @@ -132,12 +132,9 @@ export function TeamPanel({

return (
<>
<PageHeader
title={team ? "Roster" : "Create a team"}
detail={team ? team.guildName : "You become the leader and take the first of eight roster slots."}
/>
<PageHeader title={team ? "Roster" : "Create a team"} detail={team?.guildName} />
<form onSubmit={submit} className="max-w-4xl">
<Section title="Team" description={team ? undefined : "A team belongs to one Discord server."}>
<Section title="Team">
{!team && (
<Field label="Discord server">
<Select
Expand Down Expand Up @@ -177,16 +174,13 @@ export function TeamPanel({
<TimezoneCombobox value={timezone} onChange={setTimezone} />
</Field>
) : (
<Field label="Timezone" hint="Change it on the Schedule tab so raid times are reviewed together.">
<Field label="Timezone" hint="Set on the Schedule tab.">
<div className="flex h-10 items-center font-mono text-sm text-muted-foreground">{team.timezone}</div>
</Field>
)}
</Section>

<Section
title="Members"
description={`${selected.length + 1} of ${rosterLimit}. Members are Discord accounts in this server.`}
>
<Section title="Members" description={`${selected.length + 1} of ${rosterLimit}`}>
<RosterList
members={[
{ member: leader, role: "Leader" },
Expand Down Expand Up @@ -222,7 +216,6 @@ export function TeamPanel({
<div className="flex flex-wrap items-center justify-between gap-4 rounded-md border border-destructive/30 p-4">
<div>
<div className="text-sm font-medium">Delete this team</div>
<p className="mt-0.5 text-sm text-muted-foreground">Removes the schedule and all timetable history.</p>
</div>
<AlertDialog>
<AlertDialogTrigger asChild>
Expand Down
Loading