Skip to content

Commit

Permalink
style: cleaning up code
Browse files Browse the repository at this point in the history
  • Loading branch information
aacevski committed Feb 22, 2025
1 parent 3a71b4f commit 75ce378
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 201 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,6 @@ function CreateProjectModal({ open, onClose }: CreateProjectModalProps) {
pattern="[A-Z0-9]+"
required
/>
<Button
type="button"
onClick={() => setSlug(generateProjectSlug(name))}
className="bg-zinc-100 text-zinc-900 hover:bg-zinc-200 dark:bg-zinc-800 dark:text-zinc-100 dark:hover:bg-zinc-700"
>
Generate
</Button>
</div>
<p className="mt-1 text-xs text-zinc-500 dark:text-zinc-400">
This key will be used for ticket IDs (e.g., ABC-123)
Expand Down
2 changes: 0 additions & 2 deletions apps/web/src/components/team/delete-team-member-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ function DeleteTeamMemberModal({
const { mutateAsync: deleteWorkspaceUser } = useDeleteWorkspaceUser();
const queryClient = useQueryClient();

console.log({ workspaceId, userEmail });

const onRemoveMember = async () => {
await deleteWorkspaceUser({
workspaceId: workspaceId,
Expand Down
91 changes: 91 additions & 0 deletions apps/web/src/components/team/members-table.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { getStatusIcon, getStatusText } from "@/lib/status";
import type WorkspaceUser from "@/types/workspace-user";
import { Avatar, AvatarFallback } from "../ui/avatar";

type MembersTableProps = {
users: WorkspaceUser[];
};

function MembersTable({ users }: MembersTableProps) {
return (
<table className="w-full table-auto min-w-[800px]">
<thead>
<tr className="border-b border-zinc-200 dark:border-zinc-800">
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Email
</th>
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Role
</th>
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Status
</th>
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Date
</th>
<th className="w-px" />
</tr>
</thead>
<tbody>
{users?.map((member) => (
<tr
key={member.userEmail}
className="border-b border-zinc-200 dark:border-zinc-800 last:border-0"
>
<td className="py-3 px-4">
<div className="flex items-center">
<Avatar className="h-8 w-8 mr-3">
<AvatarFallback className="bg-indigo-100 dark:bg-indigo-900/50 text-indigo-600 dark:text-indigo-400">
{member.userEmail.charAt(0)}
</AvatarFallback>
</Avatar>
<span className="text-sm text-zinc-900 dark:text-zinc-100">
{member.userEmail}
</span>
</div>
</td>
<td className="py-3 px-4">
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-indigo-50 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-400">
{member.role.charAt(0).toUpperCase() +
member.role.slice(1).toLowerCase()}
</span>
</td>
<td className="py-3 px-4">
<div className="flex items-center gap-2">
{getStatusIcon(member.status as "active" | "pending")}
<span className="text-sm text-zinc-600 dark:text-zinc-400">
{getStatusText(member.status as "active" | "pending")}
</span>
</div>
</td>
<td className="py-3 px-4 text-sm text-zinc-500 dark:text-zinc-400">
{member.joinedAt &&
new Date(member.joinedAt).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
</td>
{/* TODO: Implement delete member */}
{/* <td className="py-3 px-4">
{member.role !== "owner" && isOwner && (
<button
type="button"
className="p-1 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded"
onClick={() => {
setIsRemoveMemberModalOpen(true);
setSelectedMember(member);
}}
>
<MoreHorizontal className="w-4 h-4 text-zinc-500 dark:text-zinc-400" />
</button>
)}
</td> */}
</tr>
))}
</tbody>
</table>
);
}

export default MembersTable;
72 changes: 0 additions & 72 deletions apps/web/src/components/team/team-table.tsx

This file was deleted.

21 changes: 21 additions & 0 deletions apps/web/src/lib/status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { CheckCircle2, Clock } from "lucide-react";

export function getStatusIcon(status: "active" | "pending") {
switch (status) {
case "active":
return (
<CheckCircle2 className="w-4 h-4 text-green-500 dark:text-green-400" />
);
case "pending":
return <Clock className="w-4 h-4 text-yellow-500 dark:text-yellow-400" />;
}
}

export function getStatusText(status: "active" | "pending") {
switch (status) {
case "active":
return "Active";
case "pending":
return "Pending";
}
}
125 changes: 7 additions & 118 deletions apps/web/src/routes/dashboard/teams/$workspaceId/_layout.members.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import useAuth from "@/components/providers/auth-provider/hooks/use-auth";
import DeleteTeamMemberModal from "@/components/team/delete-team-member-modal";
import InviteTeamMemberModal from "@/components/team/invite-team-member-modal";
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
import MembersTable from "@/components/team/members-table";
import { Button } from "@/components/ui/button";
import useGetWorkspaceUsers from "@/hooks/queries/workspace-users/use-get-workspace-users";
import { createFileRoute } from "@tanstack/react-router";
import { motion } from "framer-motion";
import { CheckCircle2, Clock, MoreHorizontal, UserPlus } from "lucide-react";
import { UserPlus } from "lucide-react";
import { useState } from "react";

export const Route = createFileRoute(
Expand All @@ -15,47 +13,11 @@ export const Route = createFileRoute(
component: RouteComponent,
});

const getStatusIcon = (status: "active" | "pending") => {
switch (status) {
case "active":
return (
<CheckCircle2 className="w-4 h-4 text-green-500 dark:text-green-400" />
);
case "pending":
return <Clock className="w-4 h-4 text-yellow-500 dark:text-yellow-400" />;
}
};

const getStatusText = (status: "active" | "pending") =>
({
active: "Active",
pending: "Pending",
})[status];

type WorkspaceUser = {
userEmail: string;
userName: string | null;
joinedAt: Date;
status: string;
role: string;
};

function RouteComponent() {
const { workspaceId } = Route.useParams();
const { data: users } = useGetWorkspaceUsers({ workspaceId });
const [isInviteTeamMemberModalOpen, setIsInviteTeamMemberModalOpen] =
useState(false);
const [isRemoveMemberModalOpen, setIsRemoveMemberModalOpen] = useState(false);
const [selectedMember, setSelectedMember] = useState<WorkspaceUser | null>(
null,
);

const { user } = useAuth();

const isOwner = users?.some(
(workspaceUser) =>
workspaceUser.userEmail === user?.email && workspaceUser.role === "owner",
);

return (
<motion.div
Expand All @@ -80,82 +42,7 @@ function RouteComponent() {

<div className="bg-white dark:bg-zinc-900 rounded-lg border border-zinc-200 dark:border-zinc-800 overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full table-auto min-w-[800px]">
<thead>
<tr className="border-b border-zinc-200 dark:border-zinc-800">
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Email
</th>
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Role
</th>
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Status
</th>
<th className="text-left py-3 px-4 text-sm font-medium text-zinc-500 dark:text-zinc-400">
Date
</th>
<th className="w-px" />
</tr>
</thead>
<tbody>
{users?.map((member) => (
<tr
key={member.userEmail}
className="border-b border-zinc-200 dark:border-zinc-800 last:border-0"
>
<td className="py-3 px-4">
<div className="flex items-center">
<Avatar className="h-8 w-8 mr-3">
<AvatarFallback className="bg-indigo-100 dark:bg-indigo-900/50 text-indigo-600 dark:text-indigo-400">
{member.userEmail.charAt(0)}
</AvatarFallback>
</Avatar>
<span className="text-sm text-zinc-900 dark:text-zinc-100">
{member.userEmail}
</span>
</div>
</td>
<td className="py-3 px-4">
<span className="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-indigo-50 text-indigo-700 dark:bg-indigo-500/10 dark:text-indigo-400">
{member.role.charAt(0).toUpperCase() +
member.role.slice(1).toLowerCase()}
</span>
</td>
<td className="py-3 px-4">
<div className="flex items-center gap-2">
{getStatusIcon(member.status as "active" | "pending")}
<span className="text-sm text-zinc-600 dark:text-zinc-400">
{getStatusText(member.status as "active" | "pending")}
</span>
</div>
</td>
<td className="py-3 px-4 text-sm text-zinc-500 dark:text-zinc-400">
{member.joinedAt &&
new Date(member.joinedAt).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}
</td>
<td className="py-3 px-4">
{member.role !== "owner" && isOwner && (
<button
type="button"
className="p-1 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded"
onClick={() => {
setIsRemoveMemberModalOpen(true);
setSelectedMember(member);
}}
>
<MoreHorizontal className="w-4 h-4 text-zinc-500 dark:text-zinc-400" />
</button>
)}
</td>
</tr>
))}
</tbody>
</table>
<MembersTable users={users ?? []} />

{users?.length === 0 && (
<div className="py-12 text-center">
Expand All @@ -166,11 +53,13 @@ function RouteComponent() {
)}
</div>
</div>
<DeleteTeamMemberModal

{/* TODO: Implement delete member */}
{/* <DeleteTeamMemberModal
userEmail={selectedMember?.userEmail ?? ""}
open={isRemoveMemberModalOpen}
onClose={() => setIsRemoveMemberModalOpen(false)}
/>
/> */}

<InviteTeamMemberModal
open={isInviteTeamMemberModalOpen}
Expand Down
9 changes: 9 additions & 0 deletions apps/web/src/types/workspace-user/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
type WorkspaceUser = {
userEmail: string;
userName: string | null;
joinedAt: Date;
status: string;
role: string;
};

export default WorkspaceUser;
Binary file modified bun.lockb
Binary file not shown.
Loading

0 comments on commit 75ce378

Please sign in to comment.