style: ui improvement (#1806)

* style: kanban board theming

* style: assignee ui revamp

* style: kanban board header, label , priority , state and avatar ui revamp

* style: kanban card state dropdown

* style: sidebar profile dropdown

* style: sidebar dropdown icon

* style: sidebar workspace dropdown

* style: assignee select component

* fix: state select

* style: consistent app header button
This commit is contained in:
Anmol Singh Bhatia 2023-08-11 15:50:05 +05:30 committed by GitHub
parent 289e81d6eb
commit 5c964d144a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 71 additions and 60 deletions

View File

@ -155,7 +155,7 @@ export const IssuesFilterView: React.FC = () => {
{({ open }) => (
<>
<Popover.Button
className={`group flex items-center gap-2 rounded-md border border-custom-sidebar-border-200 bg-transparent px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
className={`group flex items-center gap-2 rounded-md border border-custom-border-200 px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
open
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100"
: "text-custom-sidebar-text-200"

View File

@ -10,7 +10,7 @@ import projectService from "services/project.service";
// hooks
import useProjects from "hooks/use-projects";
// component
import { Avatar } from "components/ui";
import { Avatar, Icon } from "components/ui";
// icons
import { ArrowsPointingInIcon, ArrowsPointingOutIcon, PlusIcon } from "@heroicons/react/24/outline";
import { getPriorityIcon, getStateGroupIcon } from "components/icons";
@ -140,7 +140,7 @@ export const BoardHeader: React.FC<Props> = ({
>
<div className={`flex items-center ${isCollapsed ? "gap-1" : "flex-col gap-2"}`}>
<div
className={`flex cursor-pointer items-center gap-x-3 max-w-[316px] ${
className={`flex cursor-pointer items-center gap-x-2 max-w-[316px] ${
!isCollapsed ? "mb-2 flex-col gap-y-2 py-2" : ""
}`}
>
@ -155,11 +155,7 @@ export const BoardHeader: React.FC<Props> = ({
>
{getGroupTitle()}
</h2>
<span
className={`${
isCollapsed ? "ml-0.5" : ""
} min-w-[2.5rem] rounded-full bg-custom-background-80 py-1 text-center text-xs`}
>
<span className={`${isCollapsed ? "ml-0.5" : ""} py-1 text-center text-sm`}>
{groupedIssues?.[groupTitle].length ?? 0}
</span>
</div>
@ -174,9 +170,12 @@ export const BoardHeader: React.FC<Props> = ({
}}
>
{isCollapsed ? (
<ArrowsPointingInIcon className="h-4 w-4" />
<Icon
iconName="close_fullscreen"
className="text-base font-medium text-custom-text-900"
/>
) : (
<ArrowsPointingOutIcon className="h-4 w-4" />
<Icon iconName="open_in_full" className="text-base font-medium text-custom-text-900" />
)}
</button>
{!disableUserActions && selectedGroup !== "created_by" && (

View File

@ -232,7 +232,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
</a>
</ContextMenu>
<div
className={`mb-3 rounded bg-custom-background-90 shadow ${
className={`mb-3 rounded bg-custom-background-100 shadow ${
snapshot.isDragging ? "border-2 border-custom-primary shadow-lg" : ""
}`}
ref={provided.innerRef}
@ -301,10 +301,10 @@ export const SingleBoardIssue: React.FC<Props> = ({
{issue.project_detail.identifier}-{issue.sequence_id}
</div>
)}
<h5 className="text-sm break-words line-clamp-3">{issue.name}</h5>
<h5 className="text-sm break-words line-clamp-2">{issue.name}</h5>
</a>
</Link>
<div className="relative mt-2.5 flex flex-wrap items-center gap-2 text-xs">
<div className="mt-2.5 flex overflow-x-scroll items-center gap-2 text-xs">
{properties.priority && (
<ViewPrioritySelect
issue={issue}
@ -347,6 +347,7 @@ export const SingleBoardIssue: React.FC<Props> = ({
issue={issue}
partialUpdateIssue={partialUpdateIssue}
isNotAllowed={isNotAllowed}
customButton
user={user}
selfPositioned
/>

View File

@ -18,7 +18,7 @@ export const ViewIssueLabel: React.FC<Props> = ({ issue, maxRender = 1 }) => (
{issue.label_details.map((label, index) => (
<div
key={label.id}
className="flex cursor-default items-center rounded-md border border-custom-border-300 px-2.5 py-1 text-xs shadow-sm"
className="flex cursor-default items-center flex-shrink-0 rounded-md border border-custom-border-300 px-2.5 py-1 text-xs shadow-sm"
>
<Tooltip position="top" tooltipHeading="Label" tooltipContent={label.name}>
<div className="flex items-center gap-1.5 text-custom-text-200">
@ -35,7 +35,7 @@ export const ViewIssueLabel: React.FC<Props> = ({ issue, maxRender = 1 }) => (
))}
</>
) : (
<div className="flex cursor-default items-center rounded-md border border-custom-border-300 px-2.5 py-1 text-xs shadow-sm">
<div className="flex cursor-default items-center flex-shrink-0 rounded-md border border-custom-border-300 px-2.5 py-1 text-xs shadow-sm">
<Tooltip
position="top"
tooltipHeading="Labels"

View File

@ -120,7 +120,7 @@ export const MyIssuesViewOptions: React.FC = () => {
{({ open }) => (
<>
<Popover.Button
className={`group flex items-center gap-2 rounded-md border border-custom-sidebar-border-200 bg-transparent px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
className={`group flex items-center gap-2 rounded-md border border-custom-border-200 bg-transparent px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
open
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100"
: "text-custom-sidebar-text-200"

View File

@ -8,7 +8,7 @@ import useSWR from "swr";
import projectService from "services/project.service";
import trackEventServices from "services/track-event.service";
// ui
import { AssigneesList, Avatar, CustomSearchSelect, Tooltip } from "components/ui";
import { AssigneesList, Avatar, CustomSearchSelect, Icon, Tooltip } from "components/ui";
// icons
import { UserGroupIcon } from "@heroicons/react/24/outline";
// types
@ -73,11 +73,11 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
>
{issue.assignees && issue.assignees.length > 0 && Array.isArray(issue.assignees) ? (
<div className="-my-0.5 flex items-center justify-center gap-2">
<AssigneesList userIds={issue.assignees} length={5} showLength={true} />
<AssigneesList userIds={issue.assignees} length={3} showLength={true} />
</div>
) : (
<div className="flex items-center justify-center gap-2">
<UserGroupIcon className="h-4 w-4 text-custom-text-200" />
<div className="flex items-center justify-center gap-2 px-1.5 py-1 rounded shadow-sm border border-custom-border-300">
<Icon iconName="person" className="text-sm !leading-4" />
</div>
)}
</div>
@ -87,6 +87,7 @@ export const ViewAssigneeSelect: React.FC<Props> = ({
return (
<CustomSearchSelect
value={issue.assignees}
buttonClassName="!p-0"
onChange={(data: any) => {
const newData = issue.assignees ?? [];

View File

@ -67,14 +67,8 @@ export const ViewPrioritySelect: React.FC<Props> = ({
noBorder
? ""
: issue.priority === "urgent"
? "border-red-500/20 bg-red-500/20"
: issue.priority === "high"
? "border-orange-500/20 bg-orange-500/20"
: issue.priority === "medium"
? "border-yellow-500/20 bg-yellow-500/20"
: issue.priority === "low"
? "border-green-500/20 bg-green-500/20"
: "border-custom-border-200 bg-custom-background-80"
? "border-red-500/20 bg-red-500"
: "border-custom-border-300 bg-custom-background-100"
} items-center`}
>
<Tooltip
@ -87,7 +81,7 @@ export const ViewPrioritySelect: React.FC<Props> = ({
issue.priority && issue.priority !== "" ? issue.priority ?? "" : "None",
`text-sm ${
issue.priority === "urgent"
? "text-red-500"
? "text-white"
: issue.priority === "high"
? "text-orange-500"
: issue.priority === "medium"

View File

@ -74,9 +74,9 @@ export const ViewStateSelect: React.FC<Props> = ({
position={tooltipPosition}
>
<div className="flex items-center cursor-pointer w-full gap-2 text-custom-text-200">
<span className="h-4 w-4">
<span className="h-3.5 w-3.5">
{selectedOption &&
getStateGroupIcon(selectedOption.group, "16", "16", selectedOption.color)}
getStateGroupIcon(selectedOption.group, "14", "14", selectedOption.color)}
</span>
<span className="truncate">{selectedOption?.name ?? "State"}</span>
</div>
@ -131,6 +131,7 @@ export const ViewStateSelect: React.FC<Props> = ({
disabled={isNotAllowed}
onOpen={() => setFetchStates(true)}
noChevron
selfPositioned={selfPositioned}
/>
);
};

View File

@ -146,7 +146,7 @@ export const ProfileIssuesViewOptions: React.FC = () => {
{({ open }) => (
<>
<Popover.Button
className={`group flex items-center gap-2 rounded-md border border-custom-sidebar-border-200 bg-transparent px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
className={`group flex items-center gap-2 rounded-md border border-custom-border-200 bg-transparent px-3 py-1.5 text-xs hover:bg-custom-sidebar-background-90 hover:text-custom-sidebar-text-100 focus:outline-none duration-300 ${
open
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100"
: "text-custom-sidebar-text-200"

View File

@ -3,6 +3,8 @@ import { useRouter } from "next/router";
import useSWR from "swr";
// component
import { Icon } from "components/ui";
// services
import workspaceService from "services/workspace.service";
// icons
@ -23,12 +25,14 @@ type AvatarProps = {
export const Avatar: React.FC<AvatarProps> = ({
user,
index,
height = "20px",
width = "20px",
height = "24px",
width = "24px",
fontSize = "12px",
}) => (
<div
className={`relative rounded-full ${index && index !== 0 ? "-ml-3.5" : ""}`}
className={`relative rounded border-[0.5px] ${
index && index !== 0 ? "-ml-3.5 border-custom-border-200" : "border-transparent"
}`}
style={{
height: height,
width: width,
@ -36,8 +40,8 @@ export const Avatar: React.FC<AvatarProps> = ({
>
{user && user.avatar && user.avatar !== "" ? (
<div
className={`rounded-full border-2 ${
index ? "border-custom-border-200 bg-custom-background-80" : "border-transparent"
className={`rounded border-[0.5px] ${
index ? "border-custom-border-200 bg-custom-background-100" : "border-transparent"
}`}
style={{
height: height,
@ -46,13 +50,13 @@ export const Avatar: React.FC<AvatarProps> = ({
>
<img
src={user.avatar}
className="absolute top-0 left-0 h-full w-full object-cover rounded-full"
className="absolute top-0 left-0 h-full w-full object-cover rounded"
alt={user.display_name}
/>
</div>
) : (
<div
className="grid place-items-center rounded-full border-2 border-custom-border-200 bg-gray-700 text-xs capitalize text-white"
className="grid place-items-center text-xs capitalize text-white rounded bg-gray-700 border-[0.5px] border-custom-border-200"
style={{
height: height,
width: width,
@ -75,7 +79,7 @@ type AsigneesListProps = {
export const AssigneesList: React.FC<AsigneesListProps> = ({
users,
userIds,
length = 5,
length = 3,
showLength = true,
}) => {
const router = useRouter();
@ -88,7 +92,7 @@ export const AssigneesList: React.FC<AsigneesListProps> = ({
if ((users && users.length === 0) || (userIds && userIds.length === 0))
return (
<div className="h-5 w-5 rounded-full border-2 border-white bg-custom-background-80">
<div className="h-5 w-5 rounded border-[0.5px] border-custom-border-200 bg-custom-background-80">
<Image src={User} height="100%" width="100%" className="rounded-full" alt="No user" />
</div>
);
@ -100,7 +104,14 @@ export const AssigneesList: React.FC<AsigneesListProps> = ({
{users.slice(0, length).map((user, index) => (
<Avatar key={user?.id} user={user} index={index} />
))}
{users.length > length ? <span>+{users.length - length}</span> : null}
{users.length > length ? (
<div className="-ml-3.5 relative h-6 w-6 rounded">
<div className="grid place-items-center rounded bg-custom-background-80 text-xs capitalize h-6 w-6 text-custom-text-200 border-[0.5px] border-custom-border-300">
<Icon iconName="add" className="text-xs !leading-3 -mr-0.5" />
{users.length - length}
</div>
</div>
) : null}
</>
)}
{userIds && (
@ -112,7 +123,12 @@ export const AssigneesList: React.FC<AsigneesListProps> = ({
})}
{showLength ? (
userIds.length > length ? (
<span>+{userIds.length - length}</span>
<div className="-ml-3.5 relative h-6 w-6 rounded">
<div className="flex items-center rounded bg-custom-background-80 text-xs capitalize h-6 w-6 text-custom-text-200 border-[0.5px] border-custom-border-300">
<Icon iconName="add" className="text-xs !leading-3 -mr-0.5" />
{userIds.length - length}
</div>
</div>
) : null
) : (
""

View File

@ -146,13 +146,12 @@ export const WorkspaceSidebarDropdown = () => {
>
<Menu.Items
className="fixed left-4 z-20 mt-1 flex flex-col w-full max-w-[17rem] origin-top-left rounded-md
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 shadow-lg outline-none"
border border-custom-sidebar-border-200 bg-custom-sidebar-background-100 shadow-lg outline-none"
>
<div className="flex flex-col items-start justify-start gap-3 p-3">
<div className="text-sm text-custom-sidebar-text-200">{user?.display_name}</div>
<span className="text-sm font-semibold text-custom-sidebar-text-200">Workspace</span>
<span className="text-sm font-medium text-custom-sidebar-text-200">Workspace</span>
{workspaces ? (
<div className="flex h-full w-full flex-col items-start justify-start gap-3.5">
<div className="flex h-full w-full flex-col items-start justify-start gap-1.5">
{workspaces.length > 0 ? (
workspaces.map((workspace) => (
<Menu.Item key={workspace.id}>
@ -160,7 +159,7 @@ export const WorkspaceSidebarDropdown = () => {
<button
type="button"
onClick={() => handleWorkspaceNavigation(workspace)}
className="flex w-full items-center justify-between gap-1 rounded-md text-sm text-custom-sidebar-text-100"
className="flex w-full items-center justify-between gap-1 p-1 rounded-md text-sm text-custom-sidebar-text-100 hover:bg-custom-sidebar-background-80"
>
<div className="flex items-center justify-start gap-2.5">
<span className="relative flex h-6 w-6 items-center justify-center rounded bg-gray-700 p-2 text-xs uppercase text-white">
@ -186,9 +185,7 @@ export const WorkspaceSidebarDropdown = () => {
<span className="p-1">
<CheckIcon
className={`h-3 w-3.5 text-custom-sidebar-text-100 ${
active || workspace.id === activeWorkspace?.id
? "opacity-100"
: "opacity-0"
workspace.id === activeWorkspace?.id ? "opacity-100" : "opacity-0"
}`}
/>
</span>
@ -205,9 +202,9 @@ export const WorkspaceSidebarDropdown = () => {
onClick={() => {
router.push("/create-workspace");
}}
className="flex w-full items-center gap-1 text-sm text-custom-sidebar-text-200"
className="flex w-full items-center gap-2 px-2 py-1 text-sm text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80"
>
<PlusIcon className="h-3 w-3" />
<PlusIcon className="h-4 w-4" />
Create Workspace
</Menu.Item>
</div>
@ -263,16 +260,17 @@ export const WorkspaceSidebarDropdown = () => {
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items
className="absolute left-0 z-20 mt-1.5 flex flex-col w-52 origin-top-left rounded-md
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 p-2 divide-y divide-custom-sidebar-border-200 shadow-lg text-xs outline-none"
className="absolute left-0 z-20 mt-1.5 flex flex-col w-52 origin-top-left rounded-md
border border-custom-sidebar-border-200 bg-custom-sidebar-background-100 px-1 py-2 divide-y divide-custom-sidebar-border-200 shadow-lg text-xs outline-none"
>
<div className="flex flex-col space-y-2 pb-2">
<div className="flex flex-col gap-2.5 pb-2">
<span className="px-2 text-custom-sidebar-text-200">{user?.email}</span>
{profileLinks(workspaceSlug?.toString() ?? "", user?.id ?? "").map(
(link, index) => (
<Menu.Item key={index} as="button" type="button">
<Link href={link.link}>
<a className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
<Icon iconName={link.icon} className="!text-base" />
<Icon iconName={link.icon} className="!text-lg !leading-5" />
{link.name}
</a>
</Link>
@ -287,7 +285,7 @@ export const WorkspaceSidebarDropdown = () => {
className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80"
onClick={handleSignOut}
>
<Icon iconName="logout" className="!text-base" />
<Icon iconName="logout" className="!text-lg !leading-5" />
Sign out
</Menu.Item>
</div>

View File

@ -63,7 +63,7 @@ const ProjectIssues: NextPage = () => {
<IssuesFilterView />
<SecondaryButton
onClick={() => setAnalyticsModal(true)}
className="!py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-sidebar-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
className="!py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
outline
>
Analytics
@ -72,7 +72,7 @@ const ProjectIssues: NextPage = () => {
<Link href={`/${workspaceSlug}/projects/${projectId}/inbox/${inboxList?.[0]?.id}`}>
<a>
<SecondaryButton
className="relative !py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-sidebar-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
className="relative !py-1.5 rounded-md font-normal text-custom-sidebar-text-200 border-custom-border-200 hover:text-custom-text-100 hover:bg-custom-sidebar-background-90"
outline
>
<span>Inbox</span>
@ -97,6 +97,7 @@ const ProjectIssues: NextPage = () => {
</PrimaryButton>
</div>
}
bg="secondary"
>
<AnalyticsProjectModal isOpen={analyticsModal} onClose={() => setAnalyticsModal(false)} />
<div className="h-full w-full flex flex-col">