mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-08 20:26:08 -05:00
Link between own/main team schedule
This commit is contained in:
@@ -6,6 +6,13 @@
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--s-1);
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.weekHeading {
|
||||
font-size: var(--font-xs);
|
||||
font-weight: var(--weight-semi);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import { Users } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import type { FetcherWithComponents } from "react-router";
|
||||
import * as R from "remeda";
|
||||
import { SendouButton } from "~/components/elements/Button";
|
||||
import { LinkButton, SendouButton } from "~/components/elements/Button";
|
||||
import { toastQueue } from "~/components/elements/Toast";
|
||||
import { useUser } from "~/features/auth/core/user";
|
||||
import { useUnsavedChangesChecker } from "~/form/UnsavedChangesGuard";
|
||||
import { useDateTimeFormat } from "~/hooks/intl/useDateTimeFormat";
|
||||
import { useActionSubmit } from "~/hooks/useActionSubmit";
|
||||
import { useSearchParamsTyped } from "~/modules/search-params/hooks";
|
||||
import { teamSchedulePage } from "~/utils/urls";
|
||||
import { saveWeekSchema } from "../availability-schemas";
|
||||
import { scheduleWeekSearchParams } from "../availability-search-params";
|
||||
import type { AvailabilityEditorWeek } from "../availability-types";
|
||||
@@ -19,6 +22,7 @@ import { WeekToggle } from "./WeekToggle";
|
||||
/** The events page's "My schedule": editor with current/next week toggle, "Copy last week" prefill and save. */
|
||||
export function MySchedule({ data }: { data: MyScheduleData }) {
|
||||
const { t } = useTranslation(["schedule"]);
|
||||
const user = useUser();
|
||||
const [{ week }, setParams] = useSearchParamsTyped(scheduleWeekSearchParams);
|
||||
const [weeks, setWeeks] = React.useState<Array<AvailabilityEditorWeek>>(() =>
|
||||
data.weeks.map((editorWeek) => editorWeek.days),
|
||||
@@ -87,7 +91,23 @@ export function MySchedule({ data }: { data: MyScheduleData }) {
|
||||
return (
|
||||
<section className="stack sm" data-testid="my-schedule">
|
||||
<div className={styles.header}>
|
||||
<h2 className="text-lg mx-2">{t("schedule:editor.title")}</h2>
|
||||
<div className={styles.title}>
|
||||
<h2 className="text-lg mx-2">{t("schedule:editor.title")}</h2>
|
||||
{user?.team ? (
|
||||
<LinkButton
|
||||
to={scheduleWeekSearchParams.href(
|
||||
teamSchedulePage(user.team.customUrl),
|
||||
{ week },
|
||||
)}
|
||||
variant="minimal"
|
||||
size="miniscule"
|
||||
icon={<Users />}
|
||||
testId="team-schedule-link"
|
||||
>
|
||||
{t("schedule:editor.teamSchedule")}
|
||||
</LinkButton>
|
||||
) : null}
|
||||
</div>
|
||||
<WeekToggle
|
||||
name="my-schedule-week"
|
||||
value={week}
|
||||
|
||||
@@ -6,6 +6,20 @@
|
||||
gap: var(--s-2);
|
||||
}
|
||||
|
||||
/* narrow: the actions take a line of their own, so they split to the edges of it */
|
||||
.headerActions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--s-2);
|
||||
flex-grow: 1;
|
||||
|
||||
@container (width >= 640px) {
|
||||
flex-grow: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.heading {
|
||||
font-size: var(--font-md);
|
||||
color: var(--color-text);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import clsx from "clsx";
|
||||
import { isSameDay } from "date-fns";
|
||||
import { Flag, Pencil, Plus, Trash } from "lucide-react";
|
||||
import { CalendarClock, Flag, Pencil, Plus, Trash } from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useLoaderData, useMatches } from "react-router";
|
||||
@@ -8,7 +8,7 @@ import * as R from "remeda";
|
||||
import type * as v from "valibot";
|
||||
import { ActionButton } from "~/components/ActionButton";
|
||||
import { Alert } from "~/components/Alert";
|
||||
import { SendouButton } from "~/components/elements/Button";
|
||||
import { LinkButton, SendouButton } from "~/components/elements/Button";
|
||||
import { SendouDialog } from "~/components/elements/Dialog";
|
||||
import { FormMessage } from "~/components/FormMessage";
|
||||
import { UserLink } from "~/components/UserLink";
|
||||
@@ -24,6 +24,7 @@ import { useSearchParamsTyped } from "~/modules/search-params/hooks";
|
||||
import { databaseTimestampToDate } from "~/utils/dates";
|
||||
import invariant from "~/utils/invariant";
|
||||
import type { SendouRouteHandle } from "~/utils/remix.server";
|
||||
import { EVENTS_PAGE } from "~/utils/urls";
|
||||
import { action } from "../actions/t.$customUrl.schedule.server";
|
||||
import {
|
||||
addTeamEventSchema,
|
||||
@@ -91,11 +92,22 @@ function ScheduleWeeks({ weeks }: { weeks: Array<WeekData> }) {
|
||||
shownWeek.days[6].noonAt,
|
||||
)}
|
||||
</h2>
|
||||
<WeekToggle
|
||||
name="schedule-week"
|
||||
value={week}
|
||||
onChange={(value) => setParams({ week: value })}
|
||||
/>
|
||||
<div className={styles.headerActions}>
|
||||
<LinkButton
|
||||
to={scheduleWeekSearchParams.href(EVENTS_PAGE, { week })}
|
||||
variant="minimal"
|
||||
size="miniscule"
|
||||
icon={<CalendarClock />}
|
||||
testId="edit-availability-link"
|
||||
>
|
||||
{t("schedule:team.editAvailability")}
|
||||
</LinkButton>
|
||||
<WeekToggle
|
||||
name="schedule-week"
|
||||
value={week}
|
||||
onChange={(value) => setParams({ week: value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<TeamEvents week={shownWeek} />
|
||||
<ScheduleGrid week={shownWeek} />
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
navItem: calendar
|
||||
type: feature
|
||||
---
|
||||
The availability editor links to your team's schedule and the team schedule links back, both keeping the week you are looking at
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
test,
|
||||
} from "./helpers/playwright";
|
||||
import { EventsPage } from "./pages/calendar/events-page";
|
||||
import { TeamSchedulePage } from "./pages/team/team-schedule-page";
|
||||
|
||||
const JOINED_TOURNAMENT_NAME = "Joined Tournament";
|
||||
const ORGANIZED_TOURNAMENT_NAME = "Organized Tournament";
|
||||
@@ -120,6 +121,37 @@ test.describe("My schedule", () => {
|
||||
await isNotVisible(events.weekNotFilledMarker("current"));
|
||||
});
|
||||
|
||||
test("crosses over to the team schedule and back, keeping the week", async ({
|
||||
page,
|
||||
factories,
|
||||
}) => {
|
||||
const { customUrl } = await factories.TeamFactory.create({
|
||||
name: "Team Olive",
|
||||
memberUserIds: [ADMIN_ID],
|
||||
});
|
||||
|
||||
await impersonate(page, ADMIN_ID);
|
||||
await setTimezoneCookie(page);
|
||||
|
||||
const events = new EventsPage(page);
|
||||
await events.goto();
|
||||
|
||||
// the week the editor is showing carries over to the team schedule
|
||||
await events.locators.nextWeekToggle.click();
|
||||
await events.locators.teamScheduleLink.click();
|
||||
await expect(page).toHaveURL(
|
||||
new RegExp(`/t/${customUrl}/schedule\\?week=next`),
|
||||
);
|
||||
|
||||
const schedule = new TeamSchedulePage(page);
|
||||
await expect(schedule.locators.grid).toBeVisible();
|
||||
|
||||
// the week the schedule is showing carries over to the editor
|
||||
await schedule.locators.editAvailabilityLink.click();
|
||||
await expect(page).toHaveURL(/\/events\?week=next/);
|
||||
await expect(events.weekNotFilledMarker("next")).toBeVisible();
|
||||
});
|
||||
|
||||
test("shows a commitment as a locked block on the editor", async ({
|
||||
page,
|
||||
factories,
|
||||
|
||||
@@ -29,6 +29,11 @@ export class EventsPage {
|
||||
saveWeekButton: page.getByTestId("save-week-button"),
|
||||
copyLastWeekButton: page.getByTestId("copy-last-week-button"),
|
||||
dayEditorPopover: page.getByRole("dialog"),
|
||||
teamScheduleLink: page.getByTestId("team-schedule-link"),
|
||||
// the chip radio input is visually hidden, so the label is what clicks
|
||||
nextWeekToggle: page.locator(
|
||||
'label[for="chip-radio-my-schedule-week-next"]',
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ export class TeamSchedulePage {
|
||||
notes: page.getByTestId("schedule-note"),
|
||||
teamEvents: page.getByTestId("schedule-team-event"),
|
||||
addEventButton: page.getByTestId("add-team-event-button"),
|
||||
editAvailabilityLink: page.getByTestId("edit-availability-link"),
|
||||
// the chip radio input is visually hidden, so the label is what clicks
|
||||
nextWeekToggle: page.locator(
|
||||
'label[for="chip-radio-schedule-week-next"]',
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "Availability saved",
|
||||
"editor.saveWeek": "Save week",
|
||||
"editor.title": "My availability",
|
||||
"editor.teamSchedule": "Team schedule",
|
||||
"editor.timesInYourTimezone": "Times in your time zone",
|
||||
"editor.visibility": "Visible to your teammates and friends",
|
||||
"events.title": "Team events",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "{{amount}} unknown",
|
||||
"team.canPlay": "Team can play ({{players}}+)",
|
||||
"team.currentWeek": "This week",
|
||||
"team.editAvailability": "Edit my availability",
|
||||
"team.hidden": "Only team members can see the team schedule",
|
||||
"team.nextWeek": "Next week",
|
||||
"team.noSchedule": "No schedule",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"editor.saved": "",
|
||||
"editor.saveWeek": "",
|
||||
"editor.title": "",
|
||||
"editor.teamSchedule": "",
|
||||
"editor.timesInYourTimezone": "",
|
||||
"editor.visibility": "",
|
||||
"events.title": "",
|
||||
@@ -32,6 +33,7 @@
|
||||
"registration.summary.unknown": "",
|
||||
"team.canPlay": "",
|
||||
"team.currentWeek": "",
|
||||
"team.editAvailability": "",
|
||||
"team.hidden": "",
|
||||
"team.nextWeek": "",
|
||||
"team.noSchedule": "",
|
||||
|
||||
Reference in New Issue
Block a user