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
230 changes: 119 additions & 111 deletions apps/app/components/common/list-view/single-issue.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// react
import { useState } from "react";
// next
import Link from "next/link";
import { useRouter } from "next/router";

Expand All @@ -20,7 +23,7 @@ import {
} from "constants/common";

type Props = {
type: string;
type?: string;
issue: IIssue;
properties: Properties;
editIssue: () => void;
Expand All @@ -37,7 +40,7 @@ const SingleListIssue: React.FC<Props> = ({
removeIssue,
}) => {
const router = useRouter();
const { workspaceSlug, projectId } = router.query;
let { workspaceSlug, projectId } = router.query;

const { data: issues } = useSWR<IssueResponse>(
workspaceSlug && projectId
Expand All @@ -51,121 +54,126 @@ const SingleListIssue: React.FC<Props> = ({
const totalChildren = issues?.results.filter((i) => i.parent === issue.id).length;

return (
<div
key={issue.id}
className="flex items-center justify-between gap-2 rounded px-4 py-3 text-sm"
>
<div className="flex items-center gap-2">
<span
className={`block h-1.5 w-1.5 flex-shrink-0 rounded-full`}
style={{
backgroundColor: issue.state_detail.color,
}}
/>
<Link href={`/${workspaceSlug}/projects/${issue.project_detail.id}/issues/${issue.id}`}>
<a className="group relative flex items-center gap-2">
{properties.key && (
<span className="flex-shrink-0 text-xs text-gray-500">
{issue.project_detail?.identifier}-{issue.sequence_id}
</span>
)}
<span>{issue.name}</span>
</a>
</Link>
</div>
<div className="flex flex-shrink-0 flex-wrap items-center gap-x-1 gap-y-2 text-xs">
{properties.priority && (
<div
className={`group relative flex flex-shrink-0 cursor-pointer items-center gap-1 rounded px-2 py-1 text-xs capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
issue.priority === "urgent"
? "bg-red-100 text-red-600"
: issue.priority === "high"
? "bg-orange-100 text-orange-500"
: issue.priority === "medium"
? "bg-yellow-100 text-yellow-500"
: issue.priority === "low"
? "bg-green-100 text-green-500"
: "bg-gray-100"
}`}
>
{issue.priority ?? "None"}
<div className="absolute bottom-full right-0 z-10 mb-2 hidden whitespace-nowrap rounded-md bg-white p-2 shadow-md group-hover:block">
<h5 className="mb-1 font-medium text-gray-900">Priority</h5>
<div
className={`capitalize ${
issue.priority === "urgent"
? "text-red-600"
: issue.priority === "high"
? "text-orange-500"
: issue.priority === "medium"
? "text-yellow-500"
: issue.priority === "low"
? "text-green-500"
: ""
}`}
>
{issue.priority ?? "None"}
<>
<div
key={issue.id}
className="flex items-center justify-between gap-2 rounded px-4 py-3 text-sm"
>
<div className="flex items-center gap-2">
<span
className={`block h-1.5 w-1.5 flex-shrink-0 rounded-full`}
style={{
backgroundColor: issue.state_detail.color,
}}
/>
<Link href={`/${workspaceSlug}/projects/${issue?.project_detail?.id}/issues/${issue.id}`}>
<a className="group relative flex items-center gap-2">
{properties.key && (
<span className="flex-shrink-0 text-xs text-gray-500">
{issue.project_detail?.identifier}-{issue.sequence_id}
</span>
)}
<span>{issue.name}</span>
</a>
</Link>
</div>
<div className="flex flex-shrink-0 flex-wrap items-center gap-x-1 gap-y-2 text-xs">
{properties.priority && (
<div
className={`group relative flex flex-shrink-0 cursor-pointer items-center gap-1 rounded px-2 py-1 text-xs capitalize shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
issue.priority === "urgent"
? "bg-red-100 text-red-600"
: issue.priority === "high"
? "bg-orange-100 text-orange-500"
: issue.priority === "medium"
? "bg-yellow-100 text-yellow-500"
: issue.priority === "low"
? "bg-green-100 text-green-500"
: "bg-gray-100"
}`}
>
{/* {getPriorityIcon(issue.priority ?? "")} */}
{issue.priority ?? "None"}
<div className="absolute bottom-full right-0 z-10 mb-2 hidden whitespace-nowrap rounded-md bg-white p-2 shadow-md group-hover:block">
<h5 className="mb-1 font-medium text-gray-900">Priority</h5>
<div
className={`capitalize ${
issue.priority === "urgent"
? "text-red-600"
: issue.priority === "high"
? "text-orange-500"
: issue.priority === "medium"
? "text-yellow-500"
: issue.priority === "low"
? "text-green-500"
: ""
}`}
>
{issue.priority ?? "None"}
</div>
</div>
</div>
</div>
)}
{properties.state && (
<div className="group relative flex flex-shrink-0 cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
<span
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
style={{
backgroundColor: issue?.state_detail?.color,
}}
></span>
{addSpaceIfCamelCase(issue?.state_detail.name)}
<div className="absolute bottom-full right-0 z-10 mb-2 hidden whitespace-nowrap rounded-md bg-white p-2 shadow-md group-hover:block">
<h5 className="mb-1 font-medium">State</h5>
<div>{issue?.state_detail.name}</div>
)}
{properties.state && (
<div className="group relative flex flex-shrink-0 cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
<span
className="h-1.5 w-1.5 flex-shrink-0 rounded-full"
style={{
backgroundColor: issue?.state_detail?.color,
}}
></span>
{addSpaceIfCamelCase(issue?.state_detail.name)}
<div className="absolute bottom-full right-0 z-10 mb-2 hidden whitespace-nowrap rounded-md bg-white p-2 shadow-md group-hover:block">
<h5 className="mb-1 font-medium">State</h5>
<div>{issue?.state_detail.name}</div>
</div>
</div>
</div>
)}
{properties.due_date && (
<div
className={`group group relative flex flex-shrink-0 cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
issue.target_date === null
? ""
: issue.target_date < new Date().toISOString()
? "text-red-600"
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
}`}
>
<CalendarDaysIcon className="h-4 w-4" />
{issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"}
<div className="absolute bottom-full right-0 z-10 mb-2 hidden whitespace-nowrap rounded-md bg-white p-2 shadow-md group-hover:block">
<h5 className="mb-1 font-medium text-gray-900">Due date</h5>
<div>{renderShortNumericDateFormat(issue.target_date ?? "")}</div>
<div>
{issue.target_date &&
(issue.target_date < new Date().toISOString()
? `Due date has passed by ${findHowManyDaysLeft(issue.target_date)} days`
: findHowManyDaysLeft(issue.target_date) <= 3
? `Due date is in ${findHowManyDaysLeft(issue.target_date)} days`
: "Due date")}
)}
{properties.due_date && (
<div
className={`group group relative flex flex-shrink-0 cursor-pointer items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 ${
issue.target_date === null
? ""
: issue.target_date < new Date().toISOString()
? "text-red-600"
: findHowManyDaysLeft(issue.target_date) <= 3 && "text-orange-400"
}`}
>
<CalendarDaysIcon className="h-4 w-4" />
{issue.target_date ? renderShortNumericDateFormat(issue.target_date) : "N/A"}
<div className="absolute bottom-full right-0 z-10 mb-2 hidden whitespace-nowrap rounded-md bg-white p-2 shadow-md group-hover:block">
<h5 className="mb-1 font-medium text-gray-900">Due date</h5>
<div>{renderShortNumericDateFormat(issue.target_date ?? "")}</div>
<div>
{issue.target_date &&
(issue.target_date < new Date().toISOString()
? `Due date has passed by ${findHowManyDaysLeft(issue.target_date)} days`
: findHowManyDaysLeft(issue.target_date) <= 3
? `Due date is in ${findHowManyDaysLeft(issue.target_date)} days`
: "Due date")}
</div>
</div>
</div>
</div>
)}
{properties.sub_issue_count && projectId && (
<div className="flex flex-shrink-0 items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
{totalChildren} {totalChildren === 1 ? "sub-issue" : "sub-issues"}
</div>
)}
<CustomMenu width="auto" ellipsis>
<CustomMenu.MenuItem onClick={() => editIssue()}>Edit</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => removeIssue()}>
<>Remove from {type}</>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => handleDeleteIssue()}>
Delete permanently
</CustomMenu.MenuItem>
</CustomMenu>
)}
{properties.sub_issue_count && projectId && (
<div className="flex flex-shrink-0 items-center gap-1 rounded border px-2 py-1 text-xs shadow-sm duration-300 hover:bg-gray-100 focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500">
{totalChildren} {totalChildren === 1 ? "sub-issue" : "sub-issues"}
</div>
)}
{type && (
<CustomMenu width="auto" ellipsis>
<CustomMenu.MenuItem onClick={() => editIssue()}>Edit</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => removeIssue()}>
<>Remove from {type}</>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => handleDeleteIssue()}>
Delete permanently
</CustomMenu.MenuItem>
</CustomMenu>
)}
</div>
</div>
</div>
</>
);
};

Expand Down
27 changes: 27 additions & 0 deletions apps/app/components/onboarding/invite-members.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
type Props = {
setStep: React.Dispatch<React.SetStateAction<number>>;
};

const InviteMembers: React.FC<Props> = ({ setStep }) => {
return (
<form className="grid w-full place-items-center space-y-8">
<div className="w-full space-y-4 rounded-lg bg-white p-8 md:w-2/5">
<h2 className="text-2xl font-medium">User Details</h2>
</div>
<div className="mx-auto h-1/4 space-y-2 lg:w-1/4">
<button type="submit" className="w-full rounded-md bg-gray-200 px-4 py-2 text-sm">
Invite members
</button>
<button
type="submit"
className="w-full rounded-md bg-gray-200 px-4 py-2 text-sm"
onClick={() => setStep(4)}
>
Skip
</button>
</div>
</form>
);
};

export default InviteMembers;
106 changes: 106 additions & 0 deletions apps/app/components/onboarding/user-details.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// types
import useToast from "lib/hooks/useToast";
import userService from "lib/services/user.service";
import { useEffect } from "react";
import { Controller, useForm } from "react-hook-form";
import { IUser } from "types";
import { CustomSelect, Input } from "ui";

type Props = {
user: IUser | undefined;
setStep: React.Dispatch<React.SetStateAction<number>>;
};

const UserDetails: React.FC<Props> = ({ user, setStep }) => {
const { setToastAlert } = useToast();

const {
register,
handleSubmit,
control,
reset,
formState: { errors, isSubmitting },
} = useForm<IUser>();

const onSubmit = (formData: IUser) => {
console.log(formData);

userService
.updateUser(formData)
.then((res) => {
setToastAlert({
title: "User details updated successfully!",
type: "success",
});
setStep(2);
})
.catch((err) => {
console.log(err);
});
};

useEffect(() => {
reset({
first_name: user && user.first_name,
last_name: user && user.last_name,
});
}, [user, reset]);

return (
<form className="grid w-full place-items-center space-y-8" onSubmit={handleSubmit(onSubmit)}>
<div className="w-full space-y-4 rounded-lg bg-white p-8 md:w-2/5">
<h2 className="text-2xl font-medium">User Details</h2>
<div className="grid grid-cols-2 gap-4">
<div>
<Input
label="First name"
name="first_name"
placeholder="Enter first name"
autoComplete="off"
register={register}
validations={{
required: "First name is required",
}}
error={errors.first_name}
/>
</div>
<div>
<Input
label="Last name"
name="last_name"
placeholder="Enter last name"
autoComplete="off"
register={register}
validations={{
required: "Last name is required",
}}
error={errors.last_name}
/>
</div>
<div className="col-span-2">
<Controller
name="role"
control={control}
render={({ field }) => (
<CustomSelect {...field} label="What is your role?" input>
<CustomSelect.Option value="SDE">SDE</CustomSelect.Option>
</CustomSelect>
)}
/>
</div>
</div>
</div>
<div className="mx-auto h-1/4 lg:w-1/4">
<button
type="submit"
className="w-full rounded-md bg-gray-200 px-4 py-2 text-sm"
disabled={isSubmitting}
>
{isSubmitting ? "Updating..." : "Continue"}
</button>
</div>
</form>
);
};

export default UserDetails;
Loading