mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
chore: update analytics sidebar and header content, fix: trash box positioning (#1065)
* fix: labels dropdown on issue details page theming * style: trash box styling and positioning * chore: empty state for scope and demand analytics, show assignee name in scope graph tooltip * chore: empty state for analytics * chore: modify analytics sidebar and header
This commit is contained in:
parent
559b0cc9c8
commit
3427652c22
@ -13,7 +13,12 @@ import useToast from "hooks/use-toast";
|
|||||||
// ui
|
// ui
|
||||||
import { PrimaryButton, SecondaryButton } from "components/ui";
|
import { PrimaryButton, SecondaryButton } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { ArrowDownTrayIcon, ArrowPathIcon, UserGroupIcon } from "@heroicons/react/24/outline";
|
import {
|
||||||
|
ArrowDownTrayIcon,
|
||||||
|
ArrowPathIcon,
|
||||||
|
CalendarDaysIcon,
|
||||||
|
UserGroupIcon,
|
||||||
|
} from "@heroicons/react/24/outline";
|
||||||
import { ContrastIcon, LayerDiagonalIcon } from "components/icons";
|
import { ContrastIcon, LayerDiagonalIcon } from "components/icons";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderShortDate } from "helpers/date-time.helper";
|
import { renderShortDate } from "helpers/date-time.helper";
|
||||||
@ -106,11 +111,14 @@ export const AnalyticsSidebar: React.FC<Props> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const selectedProjects =
|
||||||
|
params.project && params.project.length > 0 ? params.project : projects.map((p) => p.id);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`p-5 pb-0 flex flex-col space-y-2 ${
|
className={`px-5 py-2.5 flex items-center justify-between space-y-2 ${
|
||||||
fullScreen
|
fullScreen
|
||||||
? "pb-5 border-l border-brand-base md:h-full md:pb-5 md:border-l md:border-brand-base md:space-y-4 overflow-hidden"
|
? "border-l border-brand-base md:h-full md:border-l md:border-brand-base md:space-y-4 overflow-hidden md:flex-col md:items-start md:py-5"
|
||||||
: ""
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -119,15 +127,27 @@ export const AnalyticsSidebar: React.FC<Props> = ({
|
|||||||
<LayerDiagonalIcon height={14} width={14} />
|
<LayerDiagonalIcon height={14} width={14} />
|
||||||
{analytics ? analytics.total : "..."} Issues
|
{analytics ? analytics.total : "..."} Issues
|
||||||
</div>
|
</div>
|
||||||
|
{isProjectLevel && (
|
||||||
|
<div className="flex items-center gap-1 bg-brand-surface-2 rounded-md px-3 py-1 text-brand-secondary text-xs">
|
||||||
|
<CalendarDaysIcon className="h-3.5 w-3.5" />
|
||||||
|
{renderShortDate(
|
||||||
|
(cycleId
|
||||||
|
? cycleDetails?.created_at
|
||||||
|
: moduleId
|
||||||
|
? moduleDetails?.created_at
|
||||||
|
: projectDetails?.created_at) ?? ""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="h-full overflow-hidden">
|
<div className="h-full overflow-hidden">
|
||||||
{fullScreen ? (
|
{fullScreen ? (
|
||||||
<>
|
<>
|
||||||
{!isProjectLevel && params.project && params.project.length > 0 && (
|
{!isProjectLevel && selectedProjects && selectedProjects.length > 0 && (
|
||||||
<div className="hidden h-full overflow-hidden md:flex md:flex-col">
|
<div className="hidden h-full overflow-hidden md:flex md:flex-col">
|
||||||
<h4 className="font-medium">Selected Projects</h4>
|
<h4 className="font-medium">Selected Projects</h4>
|
||||||
<div className="space-y-6 mt-4 h-full overflow-y-auto">
|
<div className="space-y-6 mt-4 h-full overflow-y-auto">
|
||||||
{params.project.map((projectId) => {
|
{selectedProjects.map((projectId) => {
|
||||||
const project: IProject = projects.find((p) => p.id === projectId);
|
const project: IProject = projects.find((p) => p.id === projectId);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -10,6 +10,9 @@ import { useForm } from "react-hook-form";
|
|||||||
import { Tab } from "@headlessui/react";
|
import { Tab } from "@headlessui/react";
|
||||||
// services
|
// services
|
||||||
import analyticsService from "services/analytics.service";
|
import analyticsService from "services/analytics.service";
|
||||||
|
import projectService from "services/project.service";
|
||||||
|
import cyclesService from "services/cycles.service";
|
||||||
|
import modulesService from "services/modules.service";
|
||||||
// components
|
// components
|
||||||
import { CustomAnalytics, ScopeAndDemand } from "components/analytics";
|
import { CustomAnalytics, ScopeAndDemand } from "components/analytics";
|
||||||
// icons
|
// icons
|
||||||
@ -21,7 +24,7 @@ import {
|
|||||||
// types
|
// types
|
||||||
import { IAnalyticsParams } from "types";
|
import { IAnalyticsParams } from "types";
|
||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { ANALYTICS } from "constants/fetch-keys";
|
import { ANALYTICS, CYCLE_DETAILS, MODULE_DETAILS, PROJECT_DETAILS } from "constants/fetch-keys";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
@ -59,6 +62,39 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
|||||||
workspaceSlug ? () => analyticsService.getAnalytics(workspaceSlug.toString(), params) : null
|
workspaceSlug ? () => analyticsService.getAnalytics(workspaceSlug.toString(), params) : null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { data: projectDetails } = useSWR(
|
||||||
|
workspaceSlug && projectId && !(cycleId || moduleId)
|
||||||
|
? PROJECT_DETAILS(projectId.toString())
|
||||||
|
: null,
|
||||||
|
workspaceSlug && projectId && !(cycleId || moduleId)
|
||||||
|
? () => projectService.getProject(workspaceSlug.toString(), projectId.toString())
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: cycleDetails } = useSWR(
|
||||||
|
workspaceSlug && projectId && cycleId ? CYCLE_DETAILS(cycleId.toString()) : null,
|
||||||
|
workspaceSlug && projectId && cycleId
|
||||||
|
? () =>
|
||||||
|
cyclesService.getCycleDetails(
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
projectId.toString(),
|
||||||
|
cycleId.toString()
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
|
const { data: moduleDetails } = useSWR(
|
||||||
|
workspaceSlug && projectId && moduleId ? MODULE_DETAILS(moduleId.toString()) : null,
|
||||||
|
workspaceSlug && projectId && moduleId
|
||||||
|
? () =>
|
||||||
|
modulesService.getModuleDetails(
|
||||||
|
workspaceSlug.toString(),
|
||||||
|
projectId.toString(),
|
||||||
|
moduleId.toString()
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
onClose();
|
onClose();
|
||||||
};
|
};
|
||||||
@ -74,12 +110,11 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
|||||||
fullScreen ? "rounded-lg border" : "border-l"
|
fullScreen ? "rounded-lg border" : "border-l"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div
|
<div className="flex items-center justify-between gap-4 bg-brand-base px-5 py-4 text-sm">
|
||||||
className={`flex items-center justify-between gap-2 border-b border-b-brand-base bg-brand-base p-3 text-sm ${
|
<h3 className="break-all">
|
||||||
fullScreen ? "" : "py-[1.275rem]"
|
Analytics for{" "}
|
||||||
}`}
|
{cycleId ? cycleDetails?.name : moduleId ? moduleDetails?.name : projectDetails?.name}
|
||||||
>
|
</h3>
|
||||||
<h3>Project Analytics</h3>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@ -102,7 +137,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Tab.Group as={Fragment}>
|
<Tab.Group as={Fragment}>
|
||||||
<Tab.List as="div" className="space-x-2 border-b border-brand-base px-5 py-3">
|
<Tab.List as="div" className="space-x-2 border-b border-brand-base p-5 pt-0">
|
||||||
{tabsList.map((tab) => (
|
{tabsList.map((tab) => (
|
||||||
<Tab
|
<Tab
|
||||||
key={tab}
|
key={tab}
|
||||||
@ -116,6 +151,7 @@ export const AnalyticsProjectModal: React.FC<Props> = ({ isOpen, onClose }) => {
|
|||||||
</Tab>
|
</Tab>
|
||||||
))}
|
))}
|
||||||
</Tab.List>
|
</Tab.List>
|
||||||
|
{/* <h4 className="p-5 pb-0">Analytics for</h4> */}
|
||||||
<Tab.Panels as={Fragment}>
|
<Tab.Panels as={Fragment}>
|
||||||
<Tab.Panel as={Fragment}>
|
<Tab.Panel as={Fragment}>
|
||||||
<ScopeAndDemand fullScreen={fullScreen} />
|
<ScopeAndDemand fullScreen={fullScreen} />
|
||||||
|
@ -14,32 +14,39 @@ type Props = {
|
|||||||
export const AnalyticsLeaderboard: React.FC<Props> = ({ users, title }) => (
|
export const AnalyticsLeaderboard: React.FC<Props> = ({ users, title }) => (
|
||||||
<div className="p-3 border border-brand-base rounded-[10px]">
|
<div className="p-3 border border-brand-base rounded-[10px]">
|
||||||
<h6 className="text-base font-medium">{title}</h6>
|
<h6 className="text-base font-medium">{title}</h6>
|
||||||
<div className="mt-3 space-y-3">
|
{users.length > 0 ? (
|
||||||
{users.map((user) => (
|
<div className="mt-3 space-y-3">
|
||||||
<div key={user.email ?? "None"} className="flex items-start justify-between gap-4 text-xs">
|
{users.map((user) => (
|
||||||
<div className="flex items-center gap-2">
|
<div
|
||||||
{user && user.avatar && user.avatar !== "" ? (
|
key={user.email ?? "None"}
|
||||||
<div className="rounded-full h-4 w-4 flex-shrink-0">
|
className="flex items-start justify-between gap-4 text-xs"
|
||||||
<Image
|
>
|
||||||
src={user.avatar}
|
<div className="flex items-center gap-2">
|
||||||
height="100%"
|
{user && user.avatar && user.avatar !== "" ? (
|
||||||
width="100%"
|
<div className="rounded-full h-4 w-4 flex-shrink-0">
|
||||||
className="rounded-full"
|
<Image
|
||||||
alt={user.email ?? "None"}
|
src={user.avatar}
|
||||||
/>
|
height="100%"
|
||||||
</div>
|
width="100%"
|
||||||
) : (
|
className="rounded-full"
|
||||||
<div className="grid place-items-center flex-shrink-0 rounded-full bg-gray-700 text-[11px] capitalize text-white h-4 w-4">
|
alt={user.email ?? "None"}
|
||||||
{user.firstName !== "" ? user.firstName[0] : "?"}
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
) : (
|
||||||
<span className="break-all text-brand-secondary">
|
<div className="grid place-items-center flex-shrink-0 rounded-full bg-gray-700 text-[11px] capitalize text-white h-4 w-4">
|
||||||
{user.firstName !== "" ? `${user.firstName} ${user.lastName}` : "No assignee"}
|
{user.firstName !== "" ? user.firstName[0] : "?"}
|
||||||
</span>
|
</div>
|
||||||
|
)}
|
||||||
|
<span className="break-all text-brand-secondary">
|
||||||
|
{user.firstName !== "" ? `${user.firstName} ${user.lastName}` : "No assignee"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<span className="flex-shrink-0">{user.count}</span>
|
||||||
</div>
|
</div>
|
||||||
<span className="flex-shrink-0">{user.count}</span>
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
) : (
|
||||||
</div>
|
<div className="text-brand-secondary text-center text-sm py-8">No matching data found.</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,56 +13,72 @@ export const AnalyticsScope: React.FC<Props> = ({ defaultAnalytics }) => (
|
|||||||
<div className="divide-y divide-brand-base">
|
<div className="divide-y divide-brand-base">
|
||||||
<div>
|
<div>
|
||||||
<h6 className="px-3 text-base font-medium">Pending issues</h6>
|
<h6 className="px-3 text-base font-medium">Pending issues</h6>
|
||||||
<BarGraph
|
{defaultAnalytics.pending_issue_user.length > 0 ? (
|
||||||
data={defaultAnalytics.pending_issue_user}
|
<BarGraph
|
||||||
indexBy="assignees__email"
|
data={defaultAnalytics.pending_issue_user}
|
||||||
keys={["count"]}
|
indexBy="assignees__email"
|
||||||
height="250px"
|
keys={["count"]}
|
||||||
colors={() => `#f97316`}
|
height="250px"
|
||||||
customYAxisTickValues={defaultAnalytics.pending_issue_user.map((d) => d.count)}
|
colors={() => `#f97316`}
|
||||||
tooltip={(datum) => (
|
customYAxisTickValues={defaultAnalytics.pending_issue_user.map((d) => d.count)}
|
||||||
<div className="rounded-md border border-brand-base bg-brand-surface-2 p-2 text-xs">
|
tooltip={(datum) => {
|
||||||
<span className="font-medium text-brand-secondary">
|
const assignee = defaultAnalytics.pending_issue_user.find(
|
||||||
Issue count- {datum.indexValue ?? "No assignee"}:{" "}
|
(a) => a.assignees__email === `${datum.indexValue}`
|
||||||
</span>
|
);
|
||||||
{datum.value}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
axisBottom={{
|
|
||||||
renderTick: (datum) => {
|
|
||||||
const avatar =
|
|
||||||
defaultAnalytics.pending_issue_user[datum.tickIndex].assignees__avatar ?? "";
|
|
||||||
|
|
||||||
if (avatar && avatar !== "")
|
return (
|
||||||
return (
|
<div className="rounded-md border border-brand-base bg-brand-surface-2 p-2 text-xs">
|
||||||
<g transform={`translate(${datum.x},${datum.y})`}>
|
<span className="font-medium text-brand-secondary">
|
||||||
<image
|
Issue count-{" "}
|
||||||
x={-8}
|
{assignee
|
||||||
y={10}
|
? assignee.assignees__first_name + " " + assignee.assignees__last_name
|
||||||
width={16}
|
: "No assignee"}
|
||||||
height={16}
|
:{" "}
|
||||||
xlinkHref={avatar}
|
</span>
|
||||||
style={{ clipPath: "circle(50%)" }}
|
{datum.value}
|
||||||
/>
|
</div>
|
||||||
</g>
|
);
|
||||||
);
|
}}
|
||||||
else
|
axisBottom={{
|
||||||
return (
|
renderTick: (datum) => {
|
||||||
<g transform={`translate(${datum.x},${datum.y})`}>
|
const avatar =
|
||||||
<circle cy={18} r={8} fill="#374151" />
|
defaultAnalytics.pending_issue_user[datum.tickIndex].assignees__avatar ?? "";
|
||||||
<text x={0} y={21} textAnchor="middle" fontSize={9} fill="#ffffff">
|
|
||||||
{datum.value ? `${datum.value}`.toUpperCase()[0] : "?"}
|
if (avatar && avatar !== "")
|
||||||
</text>
|
return (
|
||||||
</g>
|
<g transform={`translate(${datum.x},${datum.y})`}>
|
||||||
);
|
<image
|
||||||
},
|
x={-8}
|
||||||
}}
|
y={10}
|
||||||
margin={{ top: 20 }}
|
width={16}
|
||||||
theme={{
|
height={16}
|
||||||
background: "rgb(var(--color-bg-base))",
|
xlinkHref={avatar}
|
||||||
axis: {},
|
style={{ clipPath: "circle(50%)" }}
|
||||||
}}
|
/>
|
||||||
/>
|
</g>
|
||||||
|
);
|
||||||
|
else
|
||||||
|
return (
|
||||||
|
<g transform={`translate(${datum.x},${datum.y})`}>
|
||||||
|
<circle cy={18} r={8} fill="#374151" />
|
||||||
|
<text x={0} y={21} textAnchor="middle" fontSize={9} fill="#ffffff">
|
||||||
|
{datum.value ? `${datum.value}`.toUpperCase()[0] : "?"}
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
margin={{ top: 20 }}
|
||||||
|
theme={{
|
||||||
|
background: "rgb(var(--color-bg-base))",
|
||||||
|
axis: {},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="text-brand-secondary text-center text-sm py-8">
|
||||||
|
No matching data found.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,34 +17,38 @@ export const AnalyticsYearWiseIssues: React.FC<Props> = ({ defaultAnalytics }) =
|
|||||||
return (
|
return (
|
||||||
<div className="py-3 border border-brand-base rounded-[10px]">
|
<div className="py-3 border border-brand-base rounded-[10px]">
|
||||||
<h1 className="px-3 text-base font-medium">Issues closed in a year</h1>
|
<h1 className="px-3 text-base font-medium">Issues closed in a year</h1>
|
||||||
<LineGraph
|
{defaultAnalytics.issue_completed_month_wise.length > 0 ? (
|
||||||
data={[
|
<LineGraph
|
||||||
{
|
data={[
|
||||||
id: "issues_closed",
|
{
|
||||||
color: "rgb(var(--color-accent))",
|
id: "issues_closed",
|
||||||
data: MONTHS_LIST.map((month) => ({
|
color: "rgb(var(--color-accent))",
|
||||||
x: month.label.substring(0, 3),
|
data: MONTHS_LIST.map((month) => ({
|
||||||
y:
|
x: month.label.substring(0, 3),
|
||||||
defaultAnalytics.issue_completed_month_wise.find(
|
y:
|
||||||
(data) => data.month === month.value
|
defaultAnalytics.issue_completed_month_wise.find(
|
||||||
)?.count || 0,
|
(data) => data.month === month.value
|
||||||
})),
|
)?.count || 0,
|
||||||
},
|
})),
|
||||||
]}
|
},
|
||||||
customYAxisTickValues={defaultAnalytics.issue_completed_month_wise.map((data) => {
|
]}
|
||||||
if (quarterMonthsList.includes(data.month)) return data.count;
|
customYAxisTickValues={defaultAnalytics.issue_completed_month_wise.map((data) => {
|
||||||
|
if (quarterMonthsList.includes(data.month)) return data.count;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
})}
|
})}
|
||||||
height="300px"
|
height="300px"
|
||||||
colors={(datum) => datum.color}
|
colors={(datum) => datum.color}
|
||||||
curve="monotoneX"
|
curve="monotoneX"
|
||||||
margin={{ top: 20 }}
|
margin={{ top: 20 }}
|
||||||
theme={{
|
theme={{
|
||||||
background: "rgb(var(--color-bg-base))",
|
background: "rgb(var(--color-bg-base))",
|
||||||
}}
|
}}
|
||||||
enableArea
|
enableArea
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="text-brand-secondary text-center text-sm py-8">No matching data found.</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,7 @@ import useSWR, { mutate } from "swr";
|
|||||||
|
|
||||||
// react-beautiful-dnd
|
// react-beautiful-dnd
|
||||||
import { DragDropContext, DropResult } from "react-beautiful-dnd";
|
import { DragDropContext, DropResult } from "react-beautiful-dnd";
|
||||||
|
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
|
||||||
// services
|
// services
|
||||||
import issuesService from "services/issues.service";
|
import issuesService from "services/issues.service";
|
||||||
import stateService from "services/state.service";
|
import stateService from "services/state.service";
|
||||||
@ -19,7 +20,6 @@ import useIssuesView from "hooks/use-issues-view";
|
|||||||
// components
|
// components
|
||||||
import { AllLists, AllBoards, FilterList, CalendarView } from "components/core";
|
import { AllLists, AllBoards, FilterList, CalendarView } from "components/core";
|
||||||
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
import { CreateUpdateIssueModal, DeleteIssueModal } from "components/issues";
|
||||||
import StrictModeDroppable from "components/dnd/StrictModeDroppable";
|
|
||||||
import { CreateUpdateViewModal } from "components/views";
|
import { CreateUpdateViewModal } from "components/views";
|
||||||
import { TransferIssues, TransferIssuesModal } from "components/cycles";
|
import { TransferIssues, TransferIssuesModal } from "components/cycles";
|
||||||
// ui
|
// ui
|
||||||
@ -47,7 +47,6 @@ import {
|
|||||||
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
PROJECT_ISSUES_LIST_WITH_PARAMS,
|
||||||
STATES_LIST,
|
STATES_LIST,
|
||||||
} from "constants/fetch-keys";
|
} from "constants/fetch-keys";
|
||||||
// image
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
type?: "issue" | "cycle" | "module";
|
type?: "issue" | "cycle" | "module";
|
||||||
@ -445,27 +444,25 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
<>
|
<>
|
||||||
<div className="flex items-center justify-between gap-2 px-5 pt-3 pb-0">
|
<div className="flex items-center justify-between gap-2 px-5 pt-3 pb-0">
|
||||||
<FilterList filters={filters} setFilters={setFilters} />
|
<FilterList filters={filters} setFilters={setFilters} />
|
||||||
{areFiltersApplied && (
|
<PrimaryButton
|
||||||
<PrimaryButton
|
onClick={() => {
|
||||||
onClick={() => {
|
if (viewId) {
|
||||||
if (viewId) {
|
setFilters({}, true);
|
||||||
setFilters({}, true);
|
setToastAlert({
|
||||||
setToastAlert({
|
title: "View updated",
|
||||||
title: "View updated",
|
message: "Your view has been updated",
|
||||||
message: "Your view has been updated",
|
type: "success",
|
||||||
type: "success",
|
});
|
||||||
});
|
} else
|
||||||
} else
|
setCreateViewModal({
|
||||||
setCreateViewModal({
|
query: filters,
|
||||||
query: filters,
|
});
|
||||||
});
|
}}
|
||||||
}}
|
className="flex items-center gap-2 text-sm"
|
||||||
className="flex items-center gap-2 text-sm"
|
>
|
||||||
>
|
{!viewId && <PlusIcon className="h-4 w-4" />}
|
||||||
{!viewId && <PlusIcon className="h-4 w-4" />}
|
{viewId ? "Update" : "Save"} view
|
||||||
{viewId ? "Update" : "Save"} view
|
</PrimaryButton>
|
||||||
</PrimaryButton>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
{<div className="mt-3 border-t border-brand-base" />}
|
{<div className="mt-3 border-t border-brand-base" />}
|
||||||
</>
|
</>
|
||||||
@ -477,14 +474,14 @@ export const IssuesView: React.FC<Props> = ({
|
|||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
trashBox ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
trashBox ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"
|
||||||
} fixed top-9 right-9 z-30 flex h-28 w-96 flex-col items-center justify-center gap-2 rounded border-2 border-red-500/20 bg-red-500/20 p-3 text-xs font-medium italic text-red-500 ${
|
} fixed top-4 left-1/2 -translate-x-1/2 z-40 w-72 flex items-center justify-center gap-2 rounded border-2 border-red-500/20 bg-brand-base px-3 py-5 text-xs font-medium italic text-red-500 ${
|
||||||
snapshot.isDraggingOver ? "bg-red-500/100 text-white" : ""
|
snapshot.isDraggingOver ? "bg-red-500 blur-2xl opacity-70" : ""
|
||||||
} duration-200`}
|
} transition duration-300`}
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
{...provided.droppableProps}
|
{...provided.droppableProps}
|
||||||
>
|
>
|
||||||
<TrashIcon className="h-4 w-4" />
|
<TrashIcon className="h-4 w-4" />
|
||||||
Drop issue here to delete
|
Drop here to delete the issue.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</StrictModeDroppable>
|
</StrictModeDroppable>
|
||||||
|
@ -74,7 +74,7 @@ export const IssueAttachmentUpload = () => {
|
|||||||
onDrop,
|
onDrop,
|
||||||
maxSize: maxFileSize,
|
maxSize: maxFileSize,
|
||||||
multiple: false,
|
multiple: false,
|
||||||
disabled: isLoading
|
disabled: isLoading,
|
||||||
});
|
});
|
||||||
|
|
||||||
const fileError =
|
const fileError =
|
||||||
@ -85,8 +85,8 @@ export const IssueAttachmentUpload = () => {
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
{...getRootProps()}
|
{...getRootProps()}
|
||||||
className={`flex items-center justify-center h-[60px] cursor-pointer border-2 border-dashed border-theme text-blue-500 bg-blue-500/5 text-xs rounded-md px-4 ${
|
className={`flex items-center justify-center h-[60px] cursor-pointer border-2 border-dashed text-brand-accent bg-brand-accent/5 text-xs rounded-md px-4 ${
|
||||||
isDragActive ? "bg-theme/10" : ""
|
isDragActive ? "bg-brand-accent/10 border-brand-accent" : "border-brand-base"
|
||||||
} ${isDragReject ? "bg-red-100" : ""}`}
|
} ${isDragReject ? "bg-red-100" : ""}`}
|
||||||
>
|
>
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
|
@ -450,7 +450,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
leaveFrom="opacity-100"
|
leaveFrom="opacity-100"
|
||||||
leaveTo="opacity-0"
|
leaveTo="opacity-0"
|
||||||
>
|
>
|
||||||
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-28 w-40 overflow-auto rounded-md bg-brand-surface-2 py-1 text-xs shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
|
<Listbox.Options className="absolute right-0 z-10 mt-1 max-h-28 w-40 overflow-auto rounded-md bg-brand-surface-2 py-1 text-xs shadow-lg border border-brand-base focus:outline-none">
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
{issueLabels ? (
|
{issueLabels ? (
|
||||||
issueLabels.length > 0 ? (
|
issueLabels.length > 0 ? (
|
||||||
@ -468,8 +468,8 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
`${
|
`${
|
||||||
active || selected ? "bg-brand-surface-1" : ""
|
active || selected ? "bg-brand-surface-1" : ""
|
||||||
} ${
|
} ${
|
||||||
selected ? "font-medium" : ""
|
selected ? "" : "text-brand-secondary"
|
||||||
} flex cursor-pointer select-none items-center gap-2 truncate p-2 text-brand-base`
|
} flex cursor-pointer select-none items-center gap-2 truncate p-2`
|
||||||
}
|
}
|
||||||
value={label.id}
|
value={label.id}
|
||||||
>
|
>
|
||||||
@ -489,7 +489,7 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<div className="border-y border-brand-base bg-brand-surface-1">
|
<div className="border-y border-brand-base bg-brand-surface-1">
|
||||||
<div className="flex select-none items-center gap-2 truncate p-2 font-medium text-brand-base">
|
<div className="flex select-none items-center gap-2 truncate p-2 font-medium text-brand-base">
|
||||||
<RectangleGroupIcon className="h-3 w-3" />{" "}
|
<RectangleGroupIcon className="h-3 w-3" />
|
||||||
{label.name}
|
{label.name}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@ -497,9 +497,9 @@ export const IssueDetailsSidebar: React.FC<Props> = ({
|
|||||||
<Listbox.Option
|
<Listbox.Option
|
||||||
key={child.id}
|
key={child.id}
|
||||||
className={({ active, selected }) =>
|
className={({ active, selected }) =>
|
||||||
`${active || selected ? "bg-indigo-50" : ""} ${
|
`${active || selected ? "bg-brand-base" : ""} ${
|
||||||
selected ? "font-medium" : ""
|
selected ? "" : "text-brand-secondary"
|
||||||
} flex cursor-pointer select-none items-center gap-2 truncate p-2 text-brand-base`
|
} flex cursor-pointer select-none items-center gap-2 truncate p-2`
|
||||||
}
|
}
|
||||||
value={child.id}
|
value={child.id}
|
||||||
>
|
>
|
||||||
|
Loading…
Reference in New Issue
Block a user