ls-deno/routes/team/[id].tsx

116 lines
3.5 KiB
TypeScript
Raw Permalink Normal View History

2022-10-21 03:06:37 -05:00
import { Handlers, PageProps } from "$fresh/server.ts";
2022-11-10 16:39:48 -06:00
import { getTeam, getTeamUsers, teamUserStatus } from "@/db/mod.ts";
import { type Team, type TeamUserStatus, type User } from "@/types.ts";
2022-11-10 11:41:46 -06:00
import { type ContextState } from "@/types.ts";
2022-10-21 03:06:37 -05:00
2022-11-10 16:39:48 -06:00
interface TeamIndexProps {
status: TeamUserStatus;
2022-10-21 03:06:37 -05:00
team: Team;
users: User[];
}
2022-11-10 16:39:48 -06:00
interface TeamStatusProps {
status: TeamUserStatus;
}
type TeamPageProps = TeamIndexProps | TeamStatusProps;
2022-11-10 11:41:46 -06:00
export const handler: Handlers<TeamPageProps, ContextState> = {
2022-10-21 03:06:37 -05:00
async GET(request, context) {
2022-11-10 11:41:46 -06:00
if (!context.state.user?.id) {
// unauthenticated requests may not view teams
return await context.renderNotFound();
}
// TODO: implement this with row-level security?
// TODO: do I just use supabase at this point?
2022-11-09 16:55:27 -06:00
// TODO: only allow logged-in users to view teams (and most resources!)
// TODO: only allow users that are a member of a team to view them
// NOTE: maybe teams can be public...?
2022-10-21 03:06:37 -05:00
const { id } = context.params;
2022-11-10 11:41:46 -06:00
2022-10-21 03:06:37 -05:00
console.debug({ request, context });
try {
2022-11-10 16:39:48 -06:00
const status = await teamUserStatus(context.state.user?.id, id);
console.log("Status of this user on team:", status, id);
if (!status) {
2022-11-10 14:34:27 -06:00
return await context.renderNotFound();
2022-11-10 16:39:48 -06:00
} else if (["accepted", "manager", "owner"].includes(status)) {
// users that are not a member of a team may not view it
const team = await getTeam({ id });
const users = await getTeamUsers(team) || [];
return await context.render({ team, users, status });
} else if (["invited", "left", "removed"].includes(status)) {
return await context.render({ status });
2022-11-10 14:34:27 -06:00
}
2022-11-10 16:39:48 -06:00
return await context.renderNotFound();
2022-10-21 03:06:37 -05:00
} catch (e) {
console.error(`Error handling team page for ID '${id}'`, e);
return await context.renderNotFound();
}
},
};
2022-11-10 16:39:48 -06:00
export default function TeamPage({ data }: PageProps<TeamPageProps>) {
if ("users" in data) {
return <TeamIndex {...data}></TeamIndex>;
} else {
return <TeamStatus {...data}></TeamStatus>;
}
}
function TeamStatus({ status }: TeamStatusProps) {
return <>Team status: {status}</>;
}
function TeamIndex(
{ team: { id, displayName, createdAt }, users, status }: TeamIndexProps,
2022-10-21 03:06:37 -05:00
) {
return (
<>
<a href="/dashboard">Back to dashboard</a>
<h1>{displayName} - created {createdAt.toLocaleString()}</h1>
2022-11-10 16:39:48 -06:00
{/* <h1 class="mt-4">Administrate</h1> */}
{["owner", "manager"].includes(status) && <Manage teamId={id} />}
2022-10-21 03:06:37 -05:00
<h1 class="mt-4">Team Members</h1>
<ul>
{users.map((user) => (
<li key={user.id}>
<a href={`/user/${user.id}`}>
{(user.displayName || user.username).trim()}
</a>
</li>
))}
</ul>
</>
);
}
2022-11-10 16:39:48 -06:00
function Manage({ teamId }: { teamId: string }) {
2022-11-10 17:16:23 -06:00
// TODO: invite by email WITHOUT turning into a spambot?
2022-11-10 16:39:48 -06:00
return (
2022-11-10 17:16:23 -06:00
<details class="mt-4 max-w-lg">
<summary>Manage</summary>
<details class="mt-2">
<summary>Invite User to Team</summary>
2022-11-10 16:39:48 -06:00
<form
2022-11-10 17:16:23 -06:00
class="flex flex-col max-w-lg mt-2"
2022-11-10 16:39:48 -06:00
autocomplete="off"
method="post"
action={`/team/${teamId}/invite`}
>
2022-11-10 17:16:23 -06:00
<label for="inviteUsername">Username</label>
2022-11-10 16:39:48 -06:00
<input
2022-11-10 17:16:23 -06:00
class="mb-4"
autocomplete="new-password"
2022-11-10 16:39:48 -06:00
type="text"
placeholder="Username"
name="inviteUsername"
id="inviteUsername"
/>
<input type="submit" value="Invite" />
</form>
2022-11-10 17:16:23 -06:00
</details>
</details>
2022-11-10 16:39:48 -06:00
);
}