promote: develop to stage-release (#1589)
* fix: onboarding invitations overflow (#1575) * fix: onboarding invitations overflow * fix: user avatar in the notification card * style: update graph grid color * fix: no 'Create by me' label coming up (#1573) * feat: added new issue subscriber table * dev: notification model * feat: added CRUD operation for issue subscriber * Revert "feat: added CRUD operation for issue subscriber" This reverts commit b22e0625768f0b096b5898936ace76d6882b0736. * feat: added CRUD operation for issue subscriber * dev: notification models and operations * dev: remove delete endpoint response data * dev: notification endpoints and fix bg worker for saving notifications * feat: added list and unsubscribe function in issue subscriber * dev: filter by snoozed and response update for list and permissions * dev: update issue notifications * dev: notification segregation * dev: update notifications * dev: notification filtering * dev: add issue name in notifications * dev: notification new endpoints * fix: pushing local settings * feat: notification workflow setup and made basic UI * style: improved UX with toast alerts and other interactions refactor: changed classnames according to new theme structure, changed all icons to material icons * feat: showing un-read notification count * feat: not showing 'subscribe' button on issue created by user & assigned to user not showing 'Create by you' for view & guest of the workspace * fix: 'read' -> 'unread' heading, my issue wrong filter * feat: made snooze dropdown & modal feat: switched to calendar * fix: minor ui fixes * feat: snooze modal date/time select * fix: params for read/un-read notification * style: snooze notification modal * fix: no label for 'Create by me' * fix: no label for 'Create by me' * fix: removed console log * fix: tooltip going behind popover --------- Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> * style: tooltip on notification header actions (#1577) * style: tooltip on notification header * chore: update tooltip content --------- Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com> * fix: user migrations for back population (#1578) * fix: total notifications count (#1579) * fix: notification card (#1583) * feat: add new icons package (#1586) * feat: add material icons package * chore: replace issue view icons * chore: notification ordering (#1584) * fix: uuid error when cycle and module updates (#1585) * refactor: height of popover & api fetch call (#1587) * fix: snooze dropdown overflow (#1588) --------- Co-authored-by: Dakshesh Jain <65905942+dakshesh14@users.noreply.github.com> Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: pablohashescobar <nikhilschacko@gmail.com> Co-authored-by: Nikhil <118773738+pablohashescobar@users.noreply.github.com>
This commit is contained in:
parent
b38898753f
commit
9275e6f373
28 changed files with 736 additions and 1018 deletions
|
|
@ -30,7 +30,6 @@ class NotificationViewSet(BaseViewSet):
|
||||||
|
|
||||||
def list(self, request, slug):
|
def list(self, request, slug):
|
||||||
try:
|
try:
|
||||||
order_by = request.GET.get("order_by", "-created_at")
|
|
||||||
snoozed = request.GET.get("snoozed", "false")
|
snoozed = request.GET.get("snoozed", "false")
|
||||||
archived = request.GET.get("archived", "false")
|
archived = request.GET.get("archived", "false")
|
||||||
read = request.GET.get("read", "true")
|
read = request.GET.get("read", "true")
|
||||||
|
|
@ -40,7 +39,7 @@ class NotificationViewSet(BaseViewSet):
|
||||||
|
|
||||||
notifications = Notification.objects.filter(
|
notifications = Notification.objects.filter(
|
||||||
workspace__slug=slug, receiver_id=request.user.id
|
workspace__slug=slug, receiver_id=request.user.id
|
||||||
).order_by(order_by)
|
).order_by("snoozed_till", "-created_at")
|
||||||
|
|
||||||
# Filter for snoozed notifications
|
# Filter for snoozed notifications
|
||||||
if snoozed == "false":
|
if snoozed == "false":
|
||||||
|
|
|
||||||
|
|
@ -1028,7 +1028,12 @@ def issue_activity(
|
||||||
actor = User.objects.get(pk=actor_id)
|
actor = User.objects.get(pk=actor_id)
|
||||||
project = Project.objects.get(pk=project_id)
|
project = Project.objects.get(pk=project_id)
|
||||||
|
|
||||||
|
if type not in [
|
||||||
|
"cycle.activity.created",
|
||||||
|
"cycle.activity.deleted",
|
||||||
|
"module.activity.created",
|
||||||
|
"module.activity.deleted",
|
||||||
|
]:
|
||||||
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
|
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
|
||||||
|
|
||||||
if issue is not None:
|
if issue is not None:
|
||||||
|
|
@ -1094,6 +1099,12 @@ def issue_activity(
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
capture_exception(e)
|
capture_exception(e)
|
||||||
|
|
||||||
|
if type not in [
|
||||||
|
"cycle.activity.created",
|
||||||
|
"cycle.activity.deleted",
|
||||||
|
"module.activity.created",
|
||||||
|
"module.activity.deleted",
|
||||||
|
]:
|
||||||
# Create Notifications
|
# Create Notifications
|
||||||
bulk_notifications = []
|
bulk_notifications = []
|
||||||
|
|
||||||
|
|
@ -1114,7 +1125,11 @@ def issue_activity(
|
||||||
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
|
issue = Issue.objects.filter(pk=issue_id, project_id=project_id).first()
|
||||||
|
|
||||||
# Add bot filtering
|
# Add bot filtering
|
||||||
if issue is not None and issue.created_by_id is not None and not issue.created_by.is_bot:
|
if (
|
||||||
|
issue is not None
|
||||||
|
and issue.created_by_id is not None
|
||||||
|
and not issue.created_by.is_bot
|
||||||
|
):
|
||||||
issue_subscribers = issue_subscribers + [issue.created_by_id]
|
issue_subscribers = issue_subscribers + [issue.created_by_id]
|
||||||
|
|
||||||
for subscriber in issue_subscribers:
|
for subscriber in issue_subscribers:
|
||||||
|
|
@ -1146,7 +1161,9 @@ def issue_activity(
|
||||||
"new_value": str(issue_activity.new_value),
|
"new_value": str(issue_activity.new_value),
|
||||||
"old_value": str(issue_activity.old_value),
|
"old_value": str(issue_activity.old_value),
|
||||||
"issue_comment": str(
|
"issue_comment": str(
|
||||||
issue_activity.issue_comment.comment_stripped if issue_activity.issue_comment is not None else ""
|
issue_activity.issue_comment.comment_stripped
|
||||||
|
if issue_activity.issue_comment is not None
|
||||||
|
else ""
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import plane.db.models.user
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def onboarding_default_steps(apps, schema_editor):
|
def onboarding_default_steps(apps, schema_editor):
|
||||||
default_onboarding_schema = {
|
default_onboarding_schema = {
|
||||||
"workspace_join": True,
|
"workspace_join": True,
|
||||||
|
|
@ -23,7 +24,7 @@ def onboarding_default_steps(apps, schema_editor):
|
||||||
obj.is_tour_completed = True
|
obj.is_tour_completed = True
|
||||||
updated_user.append(obj)
|
updated_user.append(obj)
|
||||||
|
|
||||||
Model.objects.bulk_update(updated_user, ["onboarding_step"], batch_size=100)
|
Model.objects.bulk_update(updated_user, ["onboarding_step", "is_tour_completed"], batch_size=100)
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
|
|
@ -79,6 +80,7 @@ class Migration(migrations.Migration):
|
||||||
name="onboarding_step",
|
name="onboarding_step",
|
||||||
field=models.JSONField(default=plane.db.models.user.get_default_onboarding),
|
field=models.JSONField(default=plane.db.models.user.get_default_onboarding),
|
||||||
),
|
),
|
||||||
|
migrations.RunPython(onboarding_default_steps),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name="Notification",
|
name="Notification",
|
||||||
fields=[
|
fields=[
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ export const EmailCodeForm = ({ handleSignIn }: any) => {
|
||||||
Please check your inbox at <span className="font-medium">{watch("email")}</span>
|
Please check your inbox at <span className="font-medium">{watch("email")}</span>
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<form className="space-y-4 mt-10">
|
<form className="space-y-4 mt-10 sm:w-[360px] mx-auto">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Input
|
<Input
|
||||||
id="email"
|
id="email"
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,16 @@ import useEstimateOption from "hooks/use-estimate-option";
|
||||||
// components
|
// components
|
||||||
import { SelectFilters } from "components/views";
|
import { SelectFilters } from "components/views";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu, Icon, ToggleSwitch, Tooltip } from "components/ui";
|
import { CustomMenu, ToggleSwitch, Tooltip } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
|
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
||||||
import {
|
import {
|
||||||
ChevronDownIcon,
|
CalendarMonthOutlined,
|
||||||
ListBulletIcon,
|
FormatListBulletedOutlined,
|
||||||
Squares2X2Icon,
|
GridViewOutlined,
|
||||||
CalendarDaysIcon,
|
TableChartOutlined,
|
||||||
} from "@heroicons/react/24/outline";
|
WaterfallChartOutlined,
|
||||||
|
} from "@mui/icons-material";
|
||||||
// helpers
|
// helpers
|
||||||
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
import { replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
||||||
import { checkIfArraysHaveSameElements } from "helpers/array.helper";
|
import { checkIfArraysHaveSameElements } from "helpers/array.helper";
|
||||||
|
|
@ -27,26 +29,26 @@ import { Properties, TIssueViewOptions } from "types";
|
||||||
// constants
|
// constants
|
||||||
import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue";
|
import { GROUP_BY_OPTIONS, ORDER_BY_OPTIONS, FILTER_ISSUE_OPTIONS } from "constants/issue";
|
||||||
|
|
||||||
const issueViewOptions: { type: TIssueViewOptions; icon: any }[] = [
|
const issueViewOptions: { type: TIssueViewOptions; Icon: any }[] = [
|
||||||
{
|
{
|
||||||
type: "list",
|
type: "list",
|
||||||
icon: <ListBulletIcon className="h-4 w-4" />,
|
Icon: FormatListBulletedOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "kanban",
|
type: "kanban",
|
||||||
icon: <Squares2X2Icon className="h-4 w-4" />,
|
Icon: GridViewOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "calendar",
|
type: "calendar",
|
||||||
icon: <CalendarDaysIcon className="h-4 w-4" />,
|
Icon: CalendarMonthOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "spreadsheet",
|
type: "spreadsheet",
|
||||||
icon: <Icon iconName="table_chart" />,
|
Icon: TableChartOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "gantt_chart",
|
type: "gantt_chart",
|
||||||
icon: <Icon iconName="waterfall_chart" className="rotate-90" />,
|
Icon: WaterfallChartOutlined,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -98,7 +100,12 @@ export const IssuesFilterView: React.FC = () => {
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setIssueView(option.type)}
|
onClick={() => setIssueView(option.type)}
|
||||||
>
|
>
|
||||||
{option.icon}
|
<option.Icon
|
||||||
|
sx={{
|
||||||
|
fontSize: 16,
|
||||||
|
}}
|
||||||
|
className={option.type === "gantt_chart" ? "rotate-90" : ""}
|
||||||
|
/>
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
|
|
@ -177,7 +184,6 @@ export const IssuesFilterView: React.FC = () => {
|
||||||
GROUP_BY_OPTIONS.find((option) => option.key === groupByProperty)
|
GROUP_BY_OPTIONS.find((option) => option.key === groupByProperty)
|
||||||
?.name ?? "Select"
|
?.name ?? "Select"
|
||||||
}
|
}
|
||||||
width="lg"
|
|
||||||
>
|
>
|
||||||
{GROUP_BY_OPTIONS.map((option) =>
|
{GROUP_BY_OPTIONS.map((option) =>
|
||||||
issueView === "kanban" && option.key === null ? null : (
|
issueView === "kanban" && option.key === null ? null : (
|
||||||
|
|
@ -198,7 +204,6 @@ export const IssuesFilterView: React.FC = () => {
|
||||||
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
|
ORDER_BY_OPTIONS.find((option) => option.key === orderBy)?.name ??
|
||||||
"Select"
|
"Select"
|
||||||
}
|
}
|
||||||
width="lg"
|
|
||||||
>
|
>
|
||||||
{ORDER_BY_OPTIONS.map((option) =>
|
{ORDER_BY_OPTIONS.map((option) =>
|
||||||
groupByProperty === "priority" && option.key === "priority" ? null : (
|
groupByProperty === "priority" && option.key === "priority" ? null : (
|
||||||
|
|
@ -223,7 +228,6 @@ export const IssuesFilterView: React.FC = () => {
|
||||||
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters.type)
|
FILTER_ISSUE_OPTIONS.find((option) => option.key === filters.type)
|
||||||
?.name ?? "Select"
|
?.name ?? "Select"
|
||||||
}
|
}
|
||||||
width="lg"
|
|
||||||
>
|
>
|
||||||
{FILTER_ISSUE_OPTIONS.map((option) => (
|
{FILTER_ISSUE_OPTIONS.map((option) => (
|
||||||
<CustomMenu.MenuItem
|
<CustomMenu.MenuItem
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import useToast from "hooks/use-toast";
|
||||||
import { CustomMenu, Icon, Tooltip } from "components/ui";
|
import { CustomMenu, Icon, Tooltip } from "components/ui";
|
||||||
|
|
||||||
// helper
|
// helper
|
||||||
import { stripHTML, replaceUnderscoreIfSnakeCase } from "helpers/string.helper";
|
import { stripHTML, replaceUnderscoreIfSnakeCase, truncateText } from "helpers/string.helper";
|
||||||
import {
|
import {
|
||||||
formatDateDistance,
|
formatDateDistance,
|
||||||
render12HourFormatTime,
|
render12HourFormatTime,
|
||||||
|
|
@ -32,7 +32,7 @@ type NotificationCardProps = {
|
||||||
|
|
||||||
const snoozeOptions = [
|
const snoozeOptions = [
|
||||||
{
|
{
|
||||||
label: "1 days",
|
label: "1 day",
|
||||||
value: new Date(new Date().getTime() + 24 * 60 * 60 * 1000),
|
value: new Date(new Date().getTime() + 24 * 60 * 60 * 1000),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -79,34 +79,35 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||||
`/${workspaceSlug}/projects/${notification.project}/issues/${notification.data.issue.id}`
|
`/${workspaceSlug}/projects/${notification.project}/issues/${notification.data.issue.id}`
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
className={`group relative py-3 px-6 cursor-pointer ${
|
className={`group w-full flex items-center gap-4 p-3 pl-6 relative cursor-pointer ${
|
||||||
notification.read_at === null ? "bg-custom-primary-70/5" : "hover:bg-custom-background-200"
|
notification.read_at === null ? "bg-custom-primary-70/5" : "hover:bg-custom-background-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{notification.read_at === null && (
|
{notification.read_at === null && (
|
||||||
<span className="absolute top-1/2 left-2 -translate-y-1/2 w-1.5 h-1.5 bg-custom-primary-100 rounded-full" />
|
<span className="absolute top-1/2 left-2 -translate-y-1/2 w-1.5 h-1.5 bg-custom-primary-100 rounded-full" />
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-4 w-full">
|
|
||||||
<div className="relative w-12 h-12 rounded-full">
|
<div className="relative w-12 h-12 rounded-full">
|
||||||
{notification.triggered_by_details.avatar &&
|
{notification.triggered_by_details.avatar &&
|
||||||
notification.triggered_by_details.avatar !== "" ? (
|
notification.triggered_by_details.avatar !== "" ? (
|
||||||
|
<div className="h-12 w-12 rounded-full">
|
||||||
<Image
|
<Image
|
||||||
src={notification.triggered_by_details.avatar}
|
src={notification.triggered_by_details.avatar}
|
||||||
alt="profile image"
|
alt="Profile Image"
|
||||||
layout="fill"
|
layout="fill"
|
||||||
objectFit="cover"
|
objectFit="cover"
|
||||||
className="rounded-full"
|
className="rounded-full"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="w-12 h-12 bg-custom-background-100 rounded-full flex justify-center items-center">
|
<div className="w-12 h-12 bg-custom-background-80 rounded-full flex justify-center items-center">
|
||||||
<span className="text-custom-text-100 font-medium text-lg">
|
<span className="text-custom-text-100 font-medium text-lg">
|
||||||
{notification.triggered_by_details.first_name[0].toUpperCase()}
|
{notification.triggered_by_details.first_name[0].toUpperCase()}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full space-y-2.5">
|
<div className="space-y-2.5 w-full overflow-hidden">
|
||||||
<div className="text-sm">
|
<div className="text-sm w-full break-words">
|
||||||
<span className="font-semibold">
|
<span className="font-semibold">
|
||||||
{notification.triggered_by_details.first_name}{" "}
|
{notification.triggered_by_details.first_name}{" "}
|
||||||
{notification.triggered_by_details.last_name}{" "}
|
{notification.triggered_by_details.last_name}{" "}
|
||||||
|
|
@ -150,31 +151,33 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full flex justify-between text-xs">
|
<div className="flex justify-between gap-2 text-xs">
|
||||||
<p className="truncate inline max-w-lg text-custom-text-300 mr-3">
|
<p className="text-custom-text-300">
|
||||||
{notification.data.issue.identifier}-{notification.data.issue.sequence_id}{" "}
|
{truncateText(
|
||||||
{notification.data.issue.name}
|
`${notification.data.issue.identifier}-${notification.data.issue.sequence_id} ${notification.data.issue.name}`,
|
||||||
|
50
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
{notification.snoozed_till ? (
|
{notification.snoozed_till ? (
|
||||||
<p className="text-custom-text-300 flex items-center gap-x-1">
|
<p className="text-custom-text-300 flex items-center justify-end gap-x-1 flex-shrink-0">
|
||||||
<Icon iconName="schedule" />
|
<Icon iconName="schedule" className="!text-base -my-0.5" />
|
||||||
<span>
|
<span>
|
||||||
Till {renderShortDate(notification.snoozed_till)},{" "}
|
Till {renderShortDate(notification.snoozed_till)},{" "}
|
||||||
{render12HourFormatTime(notification.snoozed_till)}
|
{render12HourFormatTime(notification.snoozed_till)}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
<p className="text-custom-text-300">{formatDateDistance(notification.created_at)}</p>
|
<p className="text-custom-text-300 flex-shrink-0">
|
||||||
|
{formatDateDistance(notification.created_at)}
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="absolute py-1 gap-x-3 right-3 top-3 hidden group-hover:flex">
|
<div className="absolute py-1 gap-x-3 right-3 top-3 hidden group-hover:flex">
|
||||||
{[
|
{[
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: notification.read_at ? "Mark as Unread" : "Mark as Read",
|
name: notification.read_at ? "Mark as unread" : "Mark as read",
|
||||||
icon: "chat_bubble",
|
icon: "chat_bubble",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
markNotificationReadStatus(notification.id).then(() => {
|
markNotificationReadStatus(notification.id).then(() => {
|
||||||
|
|
@ -189,8 +192,8 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: notification.archived_at ? "Unarchive Notification" : "Archive Notification",
|
name: notification.archived_at ? "Unarchive" : "Archive",
|
||||||
icon: "archive",
|
icon: notification.archived_at ? "unarchive" : "archive",
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
markNotificationArchivedStatus(notification.id).then(() => {
|
markNotificationArchivedStatus(notification.id).then(() => {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
|
|
@ -203,7 +206,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
].map((item) => (
|
].map((item) => (
|
||||||
<Tooltip tooltipContent={item.name} position="top-left">
|
<Tooltip tooltipContent={item.name}>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|
@ -211,14 +214,15 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||||
item.onClick();
|
item.onClick();
|
||||||
}}
|
}}
|
||||||
key={item.id}
|
key={item.id}
|
||||||
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded"
|
className="text-sm flex w-full items-center gap-x-2 bg-custom-background-80 hover:bg-custom-background-100 p-0.5 rounded outline-none"
|
||||||
>
|
>
|
||||||
<Icon iconName={item.icon} className="h-5 w-5 text-custom-text-300" />
|
<Icon iconName={item.icon} className="h-5 w-5 text-custom-text-300" />
|
||||||
</button>
|
</button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Tooltip tooltipContent="Snooze Notification" position="top-left">
|
<Tooltip tooltipContent="Snooze">
|
||||||
|
<div>
|
||||||
<CustomMenu
|
<CustomMenu
|
||||||
menuButtonOnClick={(e) => {
|
menuButtonOnClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|
@ -257,6 +261,7 @@ export const NotificationCard: React.FC<NotificationCardProps> = (props) => {
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
))}
|
))}
|
||||||
</CustomMenu>
|
</CustomMenu>
|
||||||
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,10 @@ import useWorkspaceMembers from "hooks/use-workspace-members";
|
||||||
import useUserNotification from "hooks/use-user-notifications";
|
import useUserNotification from "hooks/use-user-notifications";
|
||||||
|
|
||||||
// components
|
// components
|
||||||
import { Icon, Loader, EmptyState } from "components/ui";
|
import { Icon, Loader, EmptyState, Tooltip } from "components/ui";
|
||||||
import { SnoozeNotificationModal, NotificationCard } from "components/notifications";
|
import { SnoozeNotificationModal, NotificationCard } from "components/notifications";
|
||||||
|
// icons
|
||||||
|
import { NotificationsOutlined } from "@mui/icons-material";
|
||||||
// images
|
// images
|
||||||
import emptyNotification from "public/empty-state/notification.svg";
|
import emptyNotification from "public/empty-state/notification.svg";
|
||||||
// helpers
|
// helpers
|
||||||
|
|
@ -69,7 +71,7 @@ export const NotificationPopover = () => {
|
||||||
{
|
{
|
||||||
label: "Subscribed",
|
label: "Subscribed",
|
||||||
value: "watching",
|
value: "watching",
|
||||||
unreadCount: notificationCount?.watching_notifications,
|
unreadCount: notificationCount?.watching_issues,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -96,10 +98,10 @@ export const NotificationPopover = () => {
|
||||||
className={`group flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
|
className={`group flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
|
||||||
isActive
|
isActive
|
||||||
? "bg-custom-primary-100/10 text-custom-primary-100"
|
? "bg-custom-primary-100/10 text-custom-primary-100"
|
||||||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80"
|
||||||
} ${sidebarCollapse ? "justify-center" : ""}`}
|
} ${sidebarCollapse ? "justify-center" : ""}`}
|
||||||
>
|
>
|
||||||
<Icon iconName="notifications" />
|
<NotificationsOutlined fontSize="small" />
|
||||||
{sidebarCollapse ? null : <span>Notifications</span>}
|
{sidebarCollapse ? null : <span>Notifications</span>}
|
||||||
{totalNotificationCount && totalNotificationCount > 0 ? (
|
{totalNotificationCount && totalNotificationCount > 0 ? (
|
||||||
<span className="ml-auto bg-custom-primary-300 rounded-full text-xs text-white px-1.5">
|
<span className="ml-auto bg-custom-primary-300 rounded-full text-xs text-white px-1.5">
|
||||||
|
|
@ -116,10 +118,11 @@ export const NotificationPopover = () => {
|
||||||
leaveFrom="opacity-100 translate-y-0"
|
leaveFrom="opacity-100 translate-y-0"
|
||||||
leaveTo="opacity-0 translate-y-1"
|
leaveTo="opacity-0 translate-y-1"
|
||||||
>
|
>
|
||||||
<Popover.Panel className="absolute bg-custom-background-100 flex flex-col left-0 md:left-full ml-8 z-10 top-0 md:w-[36rem] w-[20rem] h-[27rem] border border-custom-border-300 shadow-lg rounded-xl">
|
<Popover.Panel className="absolute bg-custom-background-100 flex flex-col left-0 md:left-full ml-8 z-10 top-0 md:w-[36rem] w-[20rem] h-[50vh] border border-custom-border-300 shadow-lg rounded-xl">
|
||||||
<div className="flex items-center justify-between px-5 pt-5">
|
<div className="flex items-center justify-between px-5 pt-5">
|
||||||
<h2 className="text-xl font-semibold mb-2">Notifications</h2>
|
<h2 className="text-xl font-semibold mb-2">Notifications</h2>
|
||||||
<div className="flex gap-x-4 justify-center items-center text-custom-text-200">
|
<div className="flex gap-x-4 justify-center items-center text-custom-text-200">
|
||||||
|
<Tooltip tooltipContent="Refresh">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|
@ -134,6 +137,8 @@ export const NotificationPopover = () => {
|
||||||
>
|
>
|
||||||
<Icon iconName="refresh" />
|
<Icon iconName="refresh" />
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip tooltipContent="Unread notifications">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -144,6 +149,8 @@ export const NotificationPopover = () => {
|
||||||
>
|
>
|
||||||
<Icon iconName="filter_list" />
|
<Icon iconName="filter_list" />
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip tooltipContent="Snoozed notifications">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -154,6 +161,8 @@ export const NotificationPopover = () => {
|
||||||
>
|
>
|
||||||
<Icon iconName="schedule" />
|
<Icon iconName="schedule" />
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip tooltipContent="Archived notifications">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
@ -164,6 +173,7 @@ export const NotificationPopover = () => {
|
||||||
>
|
>
|
||||||
<Icon iconName="archive" />
|
<Icon iconName="archive" />
|
||||||
</button>
|
</button>
|
||||||
|
</Tooltip>
|
||||||
<button type="button" onClick={() => closePopover()}>
|
<button type="button" onClick={() => closePopover()}>
|
||||||
<Icon iconName="close" />
|
<Icon iconName="close" />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -205,6 +215,7 @@ export const NotificationPopover = () => {
|
||||||
: "border-transparent text-custom-text-200"
|
: "border-transparent text-custom-text-200"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
{tab.label}
|
||||||
{tab.unreadCount && tab.unreadCount > 0 ? (
|
{tab.unreadCount && tab.unreadCount > 0 ? (
|
||||||
<span
|
<span
|
||||||
className={`ml-2 rounded-full text-xs px-2 py-0.5 ${
|
className={`ml-2 rounded-full text-xs px-2 py-0.5 ${
|
||||||
|
|
@ -250,7 +261,7 @@ export const NotificationPopover = () => {
|
||||||
|
|
||||||
{notifications ? (
|
{notifications ? (
|
||||||
notifications.length > 0 ? (
|
notifications.length > 0 ? (
|
||||||
<div className="divide-y divide-custom-border-100 overflow-y-auto">
|
<div className="divide-y divide-custom-border-100 overflow-y-auto h-full">
|
||||||
{notifications.map((notification) => (
|
{notifications.map((notification) => (
|
||||||
<NotificationCard
|
<NotificationCard
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
|
|
@ -273,7 +284,7 @@ export const NotificationPopover = () => {
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
) : (
|
) : (
|
||||||
<Loader className="p-5 space-y-4">
|
<Loader className="p-5 space-y-4 overflow-y-auto">
|
||||||
<Loader.Item height="50px" />
|
<Loader.Item height="50px" />
|
||||||
<Loader.Item height="50px" />
|
<Loader.Item height="50px" />
|
||||||
<Loader.Item height="50px" />
|
<Loader.Item height="50px" />
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ export const JoinWorkspaces: React.FC<Props> = ({ stepChange }) => {
|
||||||
<div className="w-full space-y-7 sm:space-y-10">
|
<div className="w-full space-y-7 sm:space-y-10">
|
||||||
<h5 className="sm:text-lg">We see that someone has invited you to</h5>
|
<h5 className="sm:text-lg">We see that someone has invited you to</h5>
|
||||||
<h4 className="text-xl sm:text-2xl font-semibold">Join a workspace</h4>
|
<h4 className="text-xl sm:text-2xl font-semibold">Join a workspace</h4>
|
||||||
<div className="md:w-3/5 space-y-4">
|
<div className="max-h-[37vh] overflow-y-auto md:w-3/5 space-y-4">
|
||||||
{invitations &&
|
{invitations &&
|
||||||
invitations.map((invitation) => {
|
invitations.map((invitation) => {
|
||||||
const isSelected = invitationsRespond.includes(invitation.id);
|
const isSelected = invitationsRespond.includes(invitation.id);
|
||||||
|
|
@ -146,7 +146,11 @@ export const JoinWorkspaces: React.FC<Props> = ({ stepChange }) => {
|
||||||
>
|
>
|
||||||
Accept & Join
|
Accept & Join
|
||||||
</PrimaryButton>
|
</PrimaryButton>
|
||||||
<SecondaryButton className="border border-none bg-transparent" size="md" onClick={finishOnboarding} >
|
<SecondaryButton
|
||||||
|
className="border border-none bg-transparent"
|
||||||
|
size="md"
|
||||||
|
onClick={finishOnboarding}
|
||||||
|
>
|
||||||
Skip for now
|
Skip for now
|
||||||
</SecondaryButton>
|
</SecondaryButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,37 @@
|
||||||
// ui
|
// icons
|
||||||
import { Icon } from "components/ui";
|
import {
|
||||||
|
ArticleOutlined,
|
||||||
|
ContrastOutlined,
|
||||||
|
DatasetOutlined,
|
||||||
|
FilterNoneOutlined,
|
||||||
|
PhotoFilterOutlined,
|
||||||
|
} from "@mui/icons-material";
|
||||||
// types
|
// types
|
||||||
import { TTourSteps } from "./root";
|
import { TTourSteps } from "./root";
|
||||||
|
|
||||||
const sidebarOptions: {
|
const sidebarOptions: {
|
||||||
key: TTourSteps;
|
key: TTourSteps;
|
||||||
icon: string;
|
Icon: any;
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
{
|
||||||
key: "issues",
|
key: "issues",
|
||||||
icon: "stack",
|
Icon: FilterNoneOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "cycles",
|
key: "cycles",
|
||||||
icon: "contrast",
|
Icon: ContrastOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "modules",
|
key: "modules",
|
||||||
icon: "dataset",
|
Icon: DatasetOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "views",
|
key: "views",
|
||||||
icon: "photo_filter",
|
Icon: PhotoFilterOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "pages",
|
key: "pages",
|
||||||
icon: "article",
|
Icon: ArticleOutlined,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -52,11 +58,10 @@ export const TourSidebar: React.FC<Props> = ({ step, setStep }) => (
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setStep(option.key)}
|
onClick={() => setStep(option.key)}
|
||||||
>
|
>
|
||||||
<Icon
|
<option.Icon
|
||||||
iconName={option.icon}
|
sx={{
|
||||||
className={`h-5 w-5 flex-shrink-0 ${
|
fontSize: 18,
|
||||||
step === option.key ? "text-custom-primary-100" : "text-custom-text-200"
|
}}
|
||||||
}`}
|
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
{option.key}
|
{option.key}
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,19 @@ import projectService from "services/project.service";
|
||||||
// hooks
|
// hooks
|
||||||
import useToast from "hooks/use-toast";
|
import useToast from "hooks/use-toast";
|
||||||
// ui
|
// ui
|
||||||
import { CustomMenu, Icon, Tooltip } from "components/ui";
|
import { CustomMenu, Tooltip } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { LinkIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
|
import { LinkIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||||
|
import {
|
||||||
|
ArchiveOutlined,
|
||||||
|
ArticleOutlined,
|
||||||
|
ContrastOutlined,
|
||||||
|
DatasetOutlined,
|
||||||
|
ExpandMoreOutlined,
|
||||||
|
FilterNoneOutlined,
|
||||||
|
PhotoFilterOutlined,
|
||||||
|
SettingsOutlined,
|
||||||
|
} from "@mui/icons-material";
|
||||||
// helpers
|
// helpers
|
||||||
import { truncateText } from "helpers/string.helper";
|
import { truncateText } from "helpers/string.helper";
|
||||||
import { renderEmoji } from "helpers/emoji.helper";
|
import { renderEmoji } from "helpers/emoji.helper";
|
||||||
|
|
@ -33,32 +43,32 @@ const navigation = (workspaceSlug: string, projectId: string) => [
|
||||||
{
|
{
|
||||||
name: "Issues",
|
name: "Issues",
|
||||||
href: `/${workspaceSlug}/projects/${projectId}/issues`,
|
href: `/${workspaceSlug}/projects/${projectId}/issues`,
|
||||||
icon: "stack",
|
Icon: FilterNoneOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Cycles",
|
name: "Cycles",
|
||||||
href: `/${workspaceSlug}/projects/${projectId}/cycles`,
|
href: `/${workspaceSlug}/projects/${projectId}/cycles`,
|
||||||
icon: "contrast",
|
Icon: ContrastOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Modules",
|
name: "Modules",
|
||||||
href: `/${workspaceSlug}/projects/${projectId}/modules`,
|
href: `/${workspaceSlug}/projects/${projectId}/modules`,
|
||||||
icon: "dataset",
|
Icon: DatasetOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Views",
|
name: "Views",
|
||||||
href: `/${workspaceSlug}/projects/${projectId}/views`,
|
href: `/${workspaceSlug}/projects/${projectId}/views`,
|
||||||
icon: "photo_filter",
|
Icon: PhotoFilterOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Pages",
|
name: "Pages",
|
||||||
href: `/${workspaceSlug}/projects/${projectId}/pages`,
|
href: `/${workspaceSlug}/projects/${projectId}/pages`,
|
||||||
icon: "article",
|
Icon: ArticleOutlined,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Settings",
|
name: "Settings",
|
||||||
href: `/${workspaceSlug}/projects/${projectId}/settings`,
|
href: `/${workspaceSlug}/projects/${projectId}/settings`,
|
||||||
icon: "settings",
|
Icon: SettingsOutlined,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -164,8 +174,8 @@ export const SingleSidebarProject: React.FC<Props> = ({
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!sidebarCollapse && (
|
{!sidebarCollapse && (
|
||||||
<Icon
|
<ExpandMoreOutlined
|
||||||
iconName="expand_more"
|
fontSize="small"
|
||||||
className={`${open ? "rotate-180" : ""} text-custom-text-200 duration-300`}
|
className={`${open ? "rotate-180" : ""} text-custom-text-200 duration-300`}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
@ -211,7 +221,7 @@ export const SingleSidebarProject: React.FC<Props> = ({
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-start gap-2">
|
<div className="flex items-center justify-start gap-2">
|
||||||
<Icon iconName="archive" className="h-4 w-4" />
|
<ArchiveOutlined fontSize="small" />
|
||||||
<span>Archived Issues</span>
|
<span>Archived Issues</span>
|
||||||
</div>
|
</div>
|
||||||
</CustomMenu.MenuItem>
|
</CustomMenu.MenuItem>
|
||||||
|
|
@ -248,13 +258,17 @@ export const SingleSidebarProject: React.FC<Props> = ({
|
||||||
disabled={!sidebarCollapse}
|
disabled={!sidebarCollapse}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`group flex items-center rounded-md px-2 py-1.5 gap-2 text-xs font-medium outline-none ${
|
className={`group flex items-center rounded-md px-2 py-1.5 gap-2.5 text-xs font-medium outline-none ${
|
||||||
router.asPath.includes(item.href)
|
router.asPath.includes(item.href)
|
||||||
? "bg-custom-primary-100/10 text-custom-primary-100"
|
? "bg-custom-primary-100/10 text-custom-primary-100"
|
||||||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
||||||
} ${sidebarCollapse ? "justify-center" : ""}`}
|
} ${sidebarCollapse ? "justify-center" : ""}`}
|
||||||
>
|
>
|
||||||
<Icon iconName={item.icon} />
|
<item.Icon
|
||||||
|
sx={{
|
||||||
|
fontSize: 18,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{!sidebarCollapse && item.name}
|
{!sidebarCollapse && item.name}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
|
||||||
|
|
@ -1,187 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
// next
|
|
||||||
import Link from "next/link";
|
|
||||||
// headless ui
|
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
|
||||||
// icons
|
|
||||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
|
||||||
import { Icon } from "./icon";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
children: React.ReactNode;
|
|
||||||
label?: string | JSX.Element;
|
|
||||||
className?: string;
|
|
||||||
ellipsis?: boolean;
|
|
||||||
verticalEllipsis?: boolean;
|
|
||||||
height?: "sm" | "md" | "rg" | "lg";
|
|
||||||
width?: "sm" | "md" | "lg" | "xl" | "auto";
|
|
||||||
textAlignment?: "left" | "center" | "right";
|
|
||||||
noBorder?: boolean;
|
|
||||||
noChevron?: boolean;
|
|
||||||
position?: "left" | "right";
|
|
||||||
verticalPosition?: "top" | "bottom";
|
|
||||||
menuItemsClassName?: string;
|
|
||||||
customButton?: JSX.Element;
|
|
||||||
menuItemsWhiteBg?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
type MenuItemProps = {
|
|
||||||
children: JSX.Element | string;
|
|
||||||
renderAs?: "button" | "a";
|
|
||||||
href?: string;
|
|
||||||
onClick?: (args?: any) => void;
|
|
||||||
className?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CustomMenu = ({
|
|
||||||
children,
|
|
||||||
label,
|
|
||||||
className = "",
|
|
||||||
ellipsis = false,
|
|
||||||
verticalEllipsis = false,
|
|
||||||
height = "md",
|
|
||||||
width = "auto",
|
|
||||||
textAlignment,
|
|
||||||
noBorder = false,
|
|
||||||
noChevron = false,
|
|
||||||
position = "right",
|
|
||||||
verticalPosition = "bottom",
|
|
||||||
menuItemsClassName = "",
|
|
||||||
customButton,
|
|
||||||
menuItemsWhiteBg = false,
|
|
||||||
}: Props) => (
|
|
||||||
<Menu as="div" className={`relative w-min whitespace-nowrap text-left ${className}`}>
|
|
||||||
{({ open }) => (
|
|
||||||
<>
|
|
||||||
{customButton ? (
|
|
||||||
<Menu.Button as="div">{customButton}</Menu.Button>
|
|
||||||
) : (
|
|
||||||
<div>
|
|
||||||
{ellipsis || verticalEllipsis ? (
|
|
||||||
<Menu.Button
|
|
||||||
type="button"
|
|
||||||
className="relative grid place-items-center rounded p-1 text-custom-text-200 hover:bg-custom-background-80 outline-none"
|
|
||||||
>
|
|
||||||
<Icon
|
|
||||||
iconName="more_horiz"
|
|
||||||
className={`${verticalEllipsis ? "rotate-90" : ""} text-brand-secondary`}
|
|
||||||
/>
|
|
||||||
</Menu.Button>
|
|
||||||
) : (
|
|
||||||
<Menu.Button
|
|
||||||
type="button"
|
|
||||||
className={`flex cursor-pointer items-center justify-between gap-1 px-2.5 py-1 text-xs duration-300 hover:bg-custom-background-80 ${
|
|
||||||
open ? "bg-custom-background-90 text-custom-text-100" : "text-custom-text-200"
|
|
||||||
} ${
|
|
||||||
textAlignment === "right"
|
|
||||||
? "text-right"
|
|
||||||
: textAlignment === "center"
|
|
||||||
? "text-center"
|
|
||||||
: "text-left"
|
|
||||||
} ${
|
|
||||||
noBorder
|
|
||||||
? "rounded-md"
|
|
||||||
: "rounded-md border border-custom-border-200 shadow-sm focus:outline-none"
|
|
||||||
} ${
|
|
||||||
width === "sm"
|
|
||||||
? "w-10"
|
|
||||||
: width === "md"
|
|
||||||
? "w-20"
|
|
||||||
: width === "lg"
|
|
||||||
? "w-32"
|
|
||||||
: width === "xl"
|
|
||||||
? "w-48"
|
|
||||||
: "w-full"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
{!noChevron && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
|
|
||||||
</Menu.Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Transition
|
|
||||||
as={React.Fragment}
|
|
||||||
enter="transition ease-out duration-100"
|
|
||||||
enterFrom="transform opacity-0 scale-95"
|
|
||||||
enterTo="transform opacity-100 scale-100"
|
|
||||||
leave="transition ease-in duration-75"
|
|
||||||
leaveFrom="transform opacity-100 scale-100"
|
|
||||||
leaveTo="transform opacity-0 scale-95"
|
|
||||||
>
|
|
||||||
<Menu.Items
|
|
||||||
className={`absolute z-20 overflow-y-scroll whitespace-nowrap rounded-md border p-1 text-xs shadow-lg focus:outline-none ${
|
|
||||||
position === "left" ? "left-0 origin-top-left" : "right-0 origin-top-right"
|
|
||||||
} ${verticalPosition === "top" ? "bottom-full mb-1" : "mt-1"} ${
|
|
||||||
height === "sm"
|
|
||||||
? "max-h-28"
|
|
||||||
: height === "md"
|
|
||||||
? "max-h-44"
|
|
||||||
: height === "rg"
|
|
||||||
? "max-h-56"
|
|
||||||
: height === "lg"
|
|
||||||
? "max-h-80"
|
|
||||||
: ""
|
|
||||||
} ${
|
|
||||||
width === "sm"
|
|
||||||
? "w-10"
|
|
||||||
: width === "md"
|
|
||||||
? "w-20"
|
|
||||||
: width === "lg"
|
|
||||||
? "w-32"
|
|
||||||
: width === "xl"
|
|
||||||
? "w-48"
|
|
||||||
: "min-w-full"
|
|
||||||
} ${
|
|
||||||
menuItemsWhiteBg
|
|
||||||
? "border-custom-border-200 bg-custom-background-100"
|
|
||||||
: "border-custom-border-200 bg-custom-background-90"
|
|
||||||
} ${menuItemsClassName}`}
|
|
||||||
>
|
|
||||||
<div className="py-1">{children}</div>
|
|
||||||
</Menu.Items>
|
|
||||||
</Transition>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
|
|
||||||
const MenuItem: React.FC<MenuItemProps> = ({
|
|
||||||
children,
|
|
||||||
renderAs,
|
|
||||||
href,
|
|
||||||
onClick,
|
|
||||||
className = "",
|
|
||||||
}) => (
|
|
||||||
<Menu.Item as="div">
|
|
||||||
{({ active, close }) =>
|
|
||||||
renderAs === "a" ? (
|
|
||||||
<Link href={href ?? ""}>
|
|
||||||
<a
|
|
||||||
className={`${className} ${
|
|
||||||
active ? "bg-custom-background-80" : ""
|
|
||||||
} hover:text-custom-text-200 inline-block w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80`}
|
|
||||||
onClick={close}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
</Link>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`${className} ${
|
|
||||||
active ? "bg-custom-background-80" : ""
|
|
||||||
} hover:text-custom-text-200 w-full select-none gap-2 truncate rounded px-1 py-1.5 text-left text-custom-text-200 hover:bg-custom-background-80`}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
|
|
||||||
CustomMenu.MenuItem = MenuItem;
|
|
||||||
|
|
||||||
export { CustomMenu };
|
|
||||||
|
|
@ -4,9 +4,10 @@ import Link from "next/link";
|
||||||
|
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
|
// ui
|
||||||
|
import { DropdownProps } from "components/ui";
|
||||||
// icons
|
// icons
|
||||||
import { DropdownProps, Icon } from "components/ui";
|
import { ExpandMoreOutlined, MoreHorizOutlined } from "@mui/icons-material";
|
||||||
import { ChevronDownIcon } from "@heroicons/react/24/outline";
|
|
||||||
|
|
||||||
export type CustomMenuProps = DropdownProps & {
|
export type CustomMenuProps = DropdownProps & {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
|
|
@ -53,8 +54,8 @@ const CustomMenu = ({
|
||||||
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-80"
|
disabled ? "cursor-not-allowed" : "cursor-pointer hover:bg-custom-background-80"
|
||||||
} ${buttonClassName}`}
|
} ${buttonClassName}`}
|
||||||
>
|
>
|
||||||
<Icon
|
<MoreHorizOutlined
|
||||||
iconName="more_horiz"
|
fontSize="small"
|
||||||
className={`${verticalEllipsis ? "rotate-90" : ""} text-custom-text-200`}
|
className={`${verticalEllipsis ? "rotate-90" : ""} text-custom-text-200`}
|
||||||
/>
|
/>
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
|
|
@ -72,7 +73,14 @@ const CustomMenu = ({
|
||||||
} ${buttonClassName}`}
|
} ${buttonClassName}`}
|
||||||
>
|
>
|
||||||
{label}
|
{label}
|
||||||
{!noChevron && <ChevronDownIcon className="h-3 w-3" aria-hidden="true" />}
|
{!noChevron && (
|
||||||
|
<ExpandMoreOutlined
|
||||||
|
sx={{
|
||||||
|
fontSize: 14,
|
||||||
|
}}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Menu.Button>
|
</Menu.Button>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,16 @@
|
||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
|
|
||||||
import { useRouter } from "next/router";
|
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import useSWR from "swr";
|
|
||||||
|
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Transition } from "@headlessui/react";
|
import { Transition } from "@headlessui/react";
|
||||||
// services
|
|
||||||
import workspaceService from "services/workspace.service";
|
|
||||||
// hooks
|
// hooks
|
||||||
import useTheme from "hooks/use-theme";
|
import useTheme from "hooks/use-theme";
|
||||||
import useUser from "hooks/use-user";
|
|
||||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||||
// components
|
|
||||||
import UpgradeToProModal from "./upgrade-to-pro-modal";
|
|
||||||
// ui
|
|
||||||
import { CircularProgress, Icon } from "components/ui";
|
|
||||||
// icons
|
// icons
|
||||||
import {
|
import { Bolt, HelpOutlineOutlined, WestOutlined } from "@mui/icons-material";
|
||||||
ArrowLongLeftIcon,
|
import { ChatBubbleOvalLeftEllipsisIcon } from "@heroicons/react/24/outline";
|
||||||
ChatBubbleOvalLeftEllipsisIcon,
|
import { DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
|
||||||
ArrowUpCircleIcon,
|
|
||||||
XMarkIcon,
|
|
||||||
} from "@heroicons/react/24/outline";
|
|
||||||
import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
|
|
||||||
// fetch-keys
|
|
||||||
import { WORKSPACE_DETAILS } from "constants/fetch-keys";
|
|
||||||
|
|
||||||
const helpOptions = [
|
const helpOptions = [
|
||||||
{
|
{
|
||||||
|
|
@ -58,109 +41,35 @@ export interface WorkspaceHelpSectionProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = ({ setSidebarActive }) => {
|
export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = ({ setSidebarActive }) => {
|
||||||
const [alert, setAlert] = useState(false);
|
|
||||||
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
|
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
|
||||||
|
|
||||||
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
|
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
const { workspaceSlug } = router.query;
|
|
||||||
|
|
||||||
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
|
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
|
||||||
|
|
||||||
useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false));
|
useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false));
|
||||||
|
|
||||||
const { user } = useUser();
|
|
||||||
|
|
||||||
const [upgradeModal, setUpgradeModal] = useState(false);
|
|
||||||
|
|
||||||
const { data: workspaceDetails } = useSWR(
|
|
||||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
|
|
||||||
workspaceSlug ? () => workspaceService.getWorkspace(workspaceSlug as string) : null
|
|
||||||
);
|
|
||||||
|
|
||||||
const issueNumber = workspaceDetails?.total_issues || 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<UpgradeToProModal
|
|
||||||
isOpen={upgradeModal}
|
|
||||||
onClose={() => setUpgradeModal(false)}
|
|
||||||
user={user}
|
|
||||||
issueNumber={issueNumber}
|
|
||||||
/>
|
|
||||||
{!sidebarCollapse && (alert || issueNumber >= 750) && (
|
|
||||||
<div
|
<div
|
||||||
className={`border-t border-custom-sidebar-border-200 p-4 ${
|
className={`flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-border-200 bg-custom-sidebar-background-100 py-2 px-4 ${
|
||||||
issueNumber >= 750
|
|
||||||
? "bg-red-500/10 text-red-600"
|
|
||||||
: issueNumber >= 500
|
|
||||||
? "bg-yellow-500/10 text-yellow-600"
|
|
||||||
: "text-green-600"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<CircularProgress progress={(issueNumber / 1024) * 100} />
|
|
||||||
<div>Free Plan</div>
|
|
||||||
{issueNumber < 750 && (
|
|
||||||
<div
|
|
||||||
className="ml-auto text-custom-text-200 cursor-pointer"
|
|
||||||
onClick={() => setAlert(false)}
|
|
||||||
>
|
|
||||||
<XMarkIcon className="h-4 w-4" />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="text-custom-text-200 text-xs mt-2">
|
|
||||||
This workspace has used {issueNumber} of its 1024 issues creation limit (
|
|
||||||
{((issueNumber / 1024) * 100).toFixed(0)}
|
|
||||||
%).
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className={`flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-border-200 bg-custom-sidebar-background-100 px-4 py-2 ${
|
|
||||||
sidebarCollapse ? "flex-col" : ""
|
sidebarCollapse ? "flex-col" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{alert || issueNumber >= 750 ? (
|
{!sidebarCollapse && (
|
||||||
<button
|
<div className="w-1/2 text-center rounded-md px-2.5 py-1.5 font-medium outline-none text-sm bg-green-500/10 text-green-500">
|
||||||
type="button"
|
Free Plan
|
||||||
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
|
</div>
|
||||||
issueNumber >= 750
|
|
||||||
? "bg-red-500/10 text-red-500"
|
|
||||||
: "bg-blue-500/10 text-custom-primary-100"
|
|
||||||
} ${sidebarCollapse ? "w-full justify-center" : ""}`}
|
|
||||||
title="Shortcuts"
|
|
||||||
onClick={() => setUpgradeModal(true)}
|
|
||||||
>
|
|
||||||
<ArrowUpCircleIcon className="h-4 w-4" />
|
|
||||||
{!sidebarCollapse && <span>Learn more</span>}
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
|
|
||||||
issueNumber >= 750
|
|
||||||
? "bg-red-500/10 text-red-600"
|
|
||||||
: issueNumber >= 500
|
|
||||||
? "bg-yellow-500/10 text-yellow-600"
|
|
||||||
: "bg-green-500/10 text-green-600"
|
|
||||||
}
|
|
||||||
${sidebarCollapse ? "w-full justify-center" : ""}`}
|
|
||||||
title="Shortcuts"
|
|
||||||
onClick={() => setAlert(true)}
|
|
||||||
>
|
|
||||||
<CircularProgress
|
|
||||||
progress={(issueNumber / 1024) * 100 > 100 ? 100 : (issueNumber / 1024) * 100}
|
|
||||||
/>
|
|
||||||
{!sidebarCollapse && <span>Free Plan</span>}
|
|
||||||
</button>
|
|
||||||
)}
|
)}
|
||||||
|
<div
|
||||||
|
className={`flex items-center gap-1 ${
|
||||||
|
sidebarCollapse ? "flex-col justify-center" : "justify-evenly w-1/2"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
|
className={`rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
|
||||||
sidebarCollapse ? "w-full justify-center" : ""
|
sidebarCollapse ? "w-full" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const e = new KeyboardEvent("keydown", {
|
const e = new KeyboardEvent("keydown", {
|
||||||
|
|
@ -168,40 +77,38 @@ export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = ({ setS
|
||||||
});
|
});
|
||||||
document.dispatchEvent(e);
|
document.dispatchEvent(e);
|
||||||
}}
|
}}
|
||||||
title="Shortcuts"
|
|
||||||
>
|
>
|
||||||
<Icon iconName="bolt" />
|
<Bolt fontSize="small" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
|
className={`rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
|
||||||
sidebarCollapse ? "w-full justify-center" : ""
|
sidebarCollapse ? "w-full" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
|
onClick={() => setIsNeedHelpOpen((prev) => !prev)}
|
||||||
title="Help"
|
|
||||||
>
|
>
|
||||||
<QuestionMarkCircleIcon className="h-4 w-4 text-custom-text-200" />
|
<HelpOutlineOutlined fontSize="small" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="flex items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 md:hidden"
|
className="rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none md:hidden"
|
||||||
onClick={() => setSidebarActive(false)}
|
onClick={() => setSidebarActive(false)}
|
||||||
>
|
>
|
||||||
<ArrowLongLeftIcon className="h-4 w-4 flex-shrink-0 text-custom-text-200 group-hover:text-custom-text-100" />
|
<WestOutlined fontSize="small" />
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className={`hidden items-center gap-3 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 md:flex ${
|
className={`hidden md:flex rounded-md p-1.5 text-custom-text-200 hover:text-custom-text-100 hover:bg-custom-background-90 outline-none ${
|
||||||
sidebarCollapse ? "w-full justify-center" : ""
|
sidebarCollapse ? "w-full" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => toggleCollapsed()}
|
onClick={() => toggleCollapsed()}
|
||||||
>
|
>
|
||||||
<ArrowLongLeftIcon
|
<WestOutlined
|
||||||
className={`h-4 w-4 flex-shrink-0 text-custom-text-200 duration-300 group-hover:text-custom-text-100 ${
|
fontSize="small"
|
||||||
sidebarCollapse ? "rotate-180" : ""
|
className={`duration-300 ${sidebarCollapse ? "rotate-180" : ""}`}
|
||||||
}`}
|
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Transition
|
<Transition
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ const userLinks = (workspaceSlug: string) => [
|
||||||
export const WorkspaceSidebarDropdown = () => {
|
export const WorkspaceSidebarDropdown = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
// fetching user details
|
|
||||||
const { user, mutateUser } = useUser();
|
const { user, mutateUser } = useUser();
|
||||||
|
|
||||||
const { collapsed: sidebarCollapse } = useThemeHook();
|
const { collapsed: sidebarCollapse } = useThemeHook();
|
||||||
|
|
@ -139,8 +139,8 @@ export const WorkspaceSidebarDropdown = () => {
|
||||||
leaveTo="transform opacity-0 scale-95"
|
leaveTo="transform opacity-0 scale-95"
|
||||||
>
|
>
|
||||||
<Menu.Items
|
<Menu.Items
|
||||||
className="fixed left-2 z-20 mt-1 flex w-full max-w-[17rem] origin-top-left flex-col rounded-md
|
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 focus:outline-none"
|
border border-custom-sidebar-border-200 bg-custom-sidebar-background-90 shadow-lg outline-none"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col items-start justify-start gap-3 p-3">
|
<div className="flex flex-col items-start justify-start gap-3 p-3">
|
||||||
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div>
|
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div>
|
||||||
|
|
|
||||||
|
|
@ -5,40 +5,45 @@ import { useRouter } from "next/router";
|
||||||
|
|
||||||
// hooks
|
// hooks
|
||||||
import useTheme from "hooks/use-theme";
|
import useTheme from "hooks/use-theme";
|
||||||
|
// components
|
||||||
import { NotificationPopover } from "components/notifications";
|
import { NotificationPopover } from "components/notifications";
|
||||||
|
// ui
|
||||||
|
import { Tooltip } from "components/ui";
|
||||||
|
// icons
|
||||||
|
import {
|
||||||
|
BarChartRounded,
|
||||||
|
GridViewOutlined,
|
||||||
|
TaskAltOutlined,
|
||||||
|
WorkOutlineOutlined,
|
||||||
|
} from "@mui/icons-material";
|
||||||
|
|
||||||
const workspaceLinks = (workspaceSlug: string) => [
|
const workspaceLinks = (workspaceSlug: string) => [
|
||||||
{
|
{
|
||||||
icon: "grid_view",
|
Icon: GridViewOutlined,
|
||||||
name: "Dashboard",
|
name: "Dashboard",
|
||||||
href: `/${workspaceSlug}`,
|
href: `/${workspaceSlug}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "bar_chart",
|
Icon: BarChartRounded,
|
||||||
name: "Analytics",
|
name: "Analytics",
|
||||||
href: `/${workspaceSlug}/analytics`,
|
href: `/${workspaceSlug}/analytics`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "work",
|
Icon: WorkOutlineOutlined,
|
||||||
name: "Projects",
|
name: "Projects",
|
||||||
href: `/${workspaceSlug}/projects`,
|
href: `/${workspaceSlug}/projects`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: "task_alt",
|
Icon: TaskAltOutlined,
|
||||||
name: "My Issues",
|
name: "My Issues",
|
||||||
href: `/${workspaceSlug}/me/my-issues`,
|
href: `/${workspaceSlug}/me/my-issues`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// components
|
|
||||||
import { Icon, Tooltip } from "components/ui";
|
|
||||||
|
|
||||||
export const WorkspaceSidebarMenu = () => {
|
export const WorkspaceSidebarMenu = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { workspaceSlug } = router.query;
|
const { workspaceSlug } = router.query;
|
||||||
|
|
||||||
// theme context
|
|
||||||
const { collapsed: sidebarCollapse } = useTheme();
|
const { collapsed: sidebarCollapse } = useTheme();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -65,7 +70,7 @@ export const WorkspaceSidebarMenu = () => {
|
||||||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
||||||
} ${sidebarCollapse ? "justify-center" : ""}`}
|
} ${sidebarCollapse ? "justify-center" : ""}`}
|
||||||
>
|
>
|
||||||
<Icon iconName={`${link.icon}`} />
|
{<link.Icon fontSize="small" />}
|
||||||
{!sidebarCollapse && link.name}
|
{!sidebarCollapse && link.name}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
|
||||||
|
|
@ -1,248 +0,0 @@
|
||||||
import React, { useState, useEffect } from "react";
|
|
||||||
// headless ui
|
|
||||||
import { Dialog, Transition } from "@headlessui/react";
|
|
||||||
// icons
|
|
||||||
import { XCircleIcon, RocketLaunchIcon } from "@heroicons/react/24/outline";
|
|
||||||
import { CheckCircleIcon } from "@heroicons/react/24/solid";
|
|
||||||
// ui
|
|
||||||
import { CircularProgress } from "components/ui";
|
|
||||||
// types
|
|
||||||
import type { ICurrentUserResponse, IWorkspace } from "types";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
supabase: any;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
user: ICurrentUserResponse | undefined;
|
|
||||||
issueNumber: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const UpgradeToProModal: React.FC<Props> = ({ isOpen, onClose, user, issueNumber }) => {
|
|
||||||
const [supabaseClient, setSupabaseClient] = useState<any>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// Create a Supabase client
|
|
||||||
if (process.env.NEXT_PUBLIC_SUPABASE_URL && process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY) {
|
|
||||||
const { createClient } = window.supabase;
|
|
||||||
const supabase = createClient(
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_URL,
|
|
||||||
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY,
|
|
||||||
{
|
|
||||||
auth: {
|
|
||||||
autoRefreshToken: false,
|
|
||||||
persistSession: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (supabase) {
|
|
||||||
setSupabaseClient(supabase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
|
||||||
|
|
||||||
const handleClose = () => {
|
|
||||||
onClose();
|
|
||||||
setIsLoading(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const proFeatures = [
|
|
||||||
"Everything in free",
|
|
||||||
"Unlimited users",
|
|
||||||
"Unlimited file uploads",
|
|
||||||
"Priority Support",
|
|
||||||
"Custom Theming",
|
|
||||||
"Access to Roadmap",
|
|
||||||
"Plane AI (GPT unlimited)",
|
|
||||||
];
|
|
||||||
|
|
||||||
const [errorMessage, setErrorMessage] = useState<null | { status: String; message: string }>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const [loader, setLoader] = useState(false);
|
|
||||||
const submitEmail = async () => {
|
|
||||||
setLoader(true);
|
|
||||||
const payload = { email: user?.email || "" };
|
|
||||||
|
|
||||||
if (supabaseClient) {
|
|
||||||
if (payload?.email) {
|
|
||||||
const emailExists = await supabaseClient
|
|
||||||
.from("web-waitlist")
|
|
||||||
.select("id,email,count")
|
|
||||||
.eq("email", payload?.email);
|
|
||||||
if (emailExists.data.length === 0) {
|
|
||||||
const emailCreation = await supabaseClient
|
|
||||||
.from("web-waitlist")
|
|
||||||
.insert([{ email: payload?.email, count: 1, last_visited: new Date() }])
|
|
||||||
.select("id,email,count");
|
|
||||||
if (emailCreation.status === 201)
|
|
||||||
setErrorMessage({ status: "success", message: "Successfully registered." });
|
|
||||||
else setErrorMessage({ status: "insert_error", message: "Insertion Error." });
|
|
||||||
} else {
|
|
||||||
const emailCountUpdate = await supabaseClient
|
|
||||||
.from("web-waitlist")
|
|
||||||
.upsert({
|
|
||||||
id: emailExists.data[0]?.id,
|
|
||||||
count: emailExists.data[0]?.count + 1,
|
|
||||||
last_visited: new Date(),
|
|
||||||
})
|
|
||||||
.select("id,email,count");
|
|
||||||
if (emailCountUpdate.status === 201)
|
|
||||||
setErrorMessage({
|
|
||||||
status: "email_already_exists",
|
|
||||||
message: "Email already exists.",
|
|
||||||
});
|
|
||||||
else setErrorMessage({ status: "update_error", message: "Update Error." });
|
|
||||||
}
|
|
||||||
} else setErrorMessage({ status: "email_required", message: "Please provide email." });
|
|
||||||
} else
|
|
||||||
setErrorMessage({
|
|
||||||
status: "supabase_error",
|
|
||||||
message: "Network error. Please try again later.",
|
|
||||||
});
|
|
||||||
|
|
||||||
setLoader(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Transition.Root show={isOpen} as={React.Fragment}>
|
|
||||||
<Dialog as="div" className="relative z-20" onClose={handleClose}>
|
|
||||||
<Transition.Child
|
|
||||||
as={React.Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0"
|
|
||||||
enterTo="opacity-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100"
|
|
||||||
leaveTo="opacity-0"
|
|
||||||
>
|
|
||||||
<div className="fixed inset-0 bg-custom-backdrop bg-opacity-50 transition-opacity" />
|
|
||||||
</Transition.Child>
|
|
||||||
|
|
||||||
<div className="fixed inset-0 z-20 overflow-y-auto">
|
|
||||||
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
|
||||||
<Transition.Child
|
|
||||||
as={React.Fragment}
|
|
||||||
enter="ease-out duration-300"
|
|
||||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leave="ease-in duration-200"
|
|
||||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
|
||||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
|
||||||
>
|
|
||||||
<Dialog.Panel className="relative transform overflow-hidden rounded-lg border border-custom-border-200 bg-custom-background-100 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-4xl">
|
|
||||||
<div className="flex flex-wrap">
|
|
||||||
<div className="w-full md:w-3/5 p-6 flex flex-col gap-y-6">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<div
|
|
||||||
className={`font-semibold outline-none text-sm mt-1.5 ${
|
|
||||||
issueNumber >= 750
|
|
||||||
? "text-red-600"
|
|
||||||
: issueNumber >= 500
|
|
||||||
? "text-yellow-600"
|
|
||||||
: "text-green-600"
|
|
||||||
}`}
|
|
||||||
title="Shortcuts"
|
|
||||||
>
|
|
||||||
<CircularProgress
|
|
||||||
progress={
|
|
||||||
(issueNumber / 1024) * 100 > 100 ? 100 : (issueNumber / 1024) * 100
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="">
|
|
||||||
<div className="font-semibold text-lg">Upgrade to pro</div>
|
|
||||||
<div className="text-custom-text-200 text-sm">
|
|
||||||
This workspace has used {issueNumber} of its 1024 issues creation limit (
|
|
||||||
{((issueNumber / 1024) * 100).toFixed(2)}%).
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
onClick={handleClose}
|
|
||||||
className="w-5 h-5 text-custom-text-200 cursor-pointer mt-1.5 md:hidden block ml-auto"
|
|
||||||
>
|
|
||||||
<XCircleIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 pt-6">
|
|
||||||
<div
|
|
||||||
className={`font-semibold outline-none text-sm mt-1.5 w-5 h-5 text-[#892FFF] flex-shrink-0`}
|
|
||||||
title="Shortcuts"
|
|
||||||
>
|
|
||||||
<RocketLaunchIcon />
|
|
||||||
</div>
|
|
||||||
<div className="">
|
|
||||||
<div className="font-semibold text-lg">Order summary</div>
|
|
||||||
<div className="text-custom-text-200 text-sm">
|
|
||||||
Priority support, file uploads, and access to premium features.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex flex-wrap my-4">
|
|
||||||
{proFeatures.map((feature, index) => (
|
|
||||||
<div key={index} className="w-1/2 py-2 flex gap-2 my-1.5">
|
|
||||||
<div className="w-5 h-5 mt-0.5 text-green-600">
|
|
||||||
<CheckCircleIcon />
|
|
||||||
</div>
|
|
||||||
<div>{feature}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="w-full md:w-2/5 bg-custom-background-90 p-6 flex flex-col">
|
|
||||||
<div className="flex justify-between items-center">
|
|
||||||
<div className="font-semibold text-lg">Summary</div>
|
|
||||||
<div
|
|
||||||
onClick={handleClose}
|
|
||||||
className="w-5 h-5 text-custom-text-200 cursor-pointer mt-1.5 hidden md:block"
|
|
||||||
>
|
|
||||||
<XCircleIcon />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-custom-text-200 text-sm mt-4">
|
|
||||||
Plane application is currently in dev-mode. We will soon introduce Pro plans
|
|
||||||
once general availability has been established. Stay tuned for more updates.
|
|
||||||
In the meantime, Plane remains free and unrestricted.
|
|
||||||
<br /> <br />
|
|
||||||
We{"'"}ll ensure a smooth transition from the community version to the Pro
|
|
||||||
plan for you.
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
disabled={loader}
|
|
||||||
onClick={() => submitEmail()}
|
|
||||||
type="button"
|
|
||||||
className="mt-5 md:mt-auto whitespace-nowrap max-w-min items-center gap-x-1 rounded-md px-3 py-2 font-medium outline-none text-sm bg-custom-primary-100 text-white"
|
|
||||||
>
|
|
||||||
{loader ? "Loading.." : " Join waitlist"}
|
|
||||||
</button>
|
|
||||||
{errorMessage && (
|
|
||||||
<div
|
|
||||||
className={`mt-1 text-sm ${
|
|
||||||
errorMessage && errorMessage?.status === "success"
|
|
||||||
? "text-green-500"
|
|
||||||
: " text-red-500"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{errorMessage?.message}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog.Panel>
|
|
||||||
</Transition.Child>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Dialog>
|
|
||||||
</Transition.Root>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default UpgradeToProModal;
|
|
||||||
|
|
@ -17,12 +17,12 @@ export const CHARTS_THEME: Theme = {
|
||||||
background: "rgb(var(--color-background-80))",
|
background: "rgb(var(--color-background-80))",
|
||||||
color: "rgb(var(--color-text-200))",
|
color: "rgb(var(--color-text-200))",
|
||||||
fontSize: "0.8rem",
|
fontSize: "0.8rem",
|
||||||
border: "1px solid rgb(var(--color-background-80))",
|
border: "1px solid rgb(var(--color-border-300))",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
line: {
|
line: {
|
||||||
stroke: "rgb(var(--color-background-80))",
|
stroke: "rgb(var(--color-border-100))",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ export const render12HourFormatTime = (date: string | Date): string => {
|
||||||
if (hours > 12) hours -= 12;
|
if (hours > 12) hours -= 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
return hours + ":" + minutes + " " + period;
|
return hours + ":" + (minutes < 10 ? `0${minutes}` : minutes) + " " + period;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const render24HourFormatTime = (date: string | Date): string => {
|
export const render24HourFormatTime = (date: string | Date): string => {
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,8 @@ const useUserNotification = () => {
|
||||||
notifications?.find((notification) => notification.id === notificationId)?.read_at !== null;
|
notifications?.find((notification) => notification.id === notificationId)?.read_at !== null;
|
||||||
|
|
||||||
if (isRead) {
|
if (isRead) {
|
||||||
await userNotificationServices
|
notificationsMutate(
|
||||||
.markUserNotificationAsUnread(workspaceSlug.toString(), notificationId)
|
(prev) =>
|
||||||
.then(() => {
|
|
||||||
notificationsMutate((prev) =>
|
|
||||||
prev?.map((prevNotification) => {
|
prev?.map((prevNotification) => {
|
||||||
if (prevNotification.id === notificationId) {
|
if (prevNotification.id === notificationId) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -66,18 +64,20 @@ const useUserNotification = () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return prevNotification;
|
return prevNotification;
|
||||||
})
|
}),
|
||||||
|
false
|
||||||
);
|
);
|
||||||
mutateNotificationCount();
|
await userNotificationServices
|
||||||
})
|
.markUserNotificationAsUnread(workspaceSlug.toString(), notificationId)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new Error("Something went wrong");
|
throw new Error("Something went wrong");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
notificationsMutate();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
await userNotificationServices
|
notificationsMutate(
|
||||||
.markUserNotificationAsRead(workspaceSlug.toString(), notificationId)
|
(prev) =>
|
||||||
.then(() => {
|
|
||||||
notificationsMutate((prev) =>
|
|
||||||
prev?.map((prevNotification) => {
|
prev?.map((prevNotification) => {
|
||||||
if (prevNotification.id === notificationId) {
|
if (prevNotification.id === notificationId) {
|
||||||
return {
|
return {
|
||||||
|
|
@ -86,12 +86,16 @@ const useUserNotification = () => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return prevNotification;
|
return prevNotification;
|
||||||
})
|
}),
|
||||||
|
false
|
||||||
);
|
);
|
||||||
mutateNotificationCount();
|
await userNotificationServices
|
||||||
})
|
.markUserNotificationAsRead(workspaceSlug.toString(), notificationId)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new Error("Something went wrong");
|
throw new Error("Something went wrong");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
notificationsMutate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -105,22 +109,24 @@ const useUserNotification = () => {
|
||||||
if (isArchived) {
|
if (isArchived) {
|
||||||
await userNotificationServices
|
await userNotificationServices
|
||||||
.markUserNotificationAsUnarchived(workspaceSlug.toString(), notificationId)
|
.markUserNotificationAsUnarchived(workspaceSlug.toString(), notificationId)
|
||||||
.then(() => {
|
|
||||||
notificationsMutate();
|
|
||||||
})
|
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new Error("Something went wrong");
|
throw new Error("Something went wrong");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
notificationsMutate();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
notificationsMutate(
|
||||||
|
(prev) => prev?.filter((prevNotification) => prevNotification.id !== notificationId),
|
||||||
|
false
|
||||||
|
);
|
||||||
await userNotificationServices
|
await userNotificationServices
|
||||||
.markUserNotificationAsArchived(workspaceSlug.toString(), notificationId)
|
.markUserNotificationAsArchived(workspaceSlug.toString(), notificationId)
|
||||||
.then(() => {
|
|
||||||
notificationsMutate((prev) =>
|
|
||||||
prev?.filter((prevNotification) => prevNotification.id !== notificationId)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
throw new Error("Something went wrong");
|
throw new Error("Something went wrong");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
notificationsMutate();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -137,19 +143,25 @@ const useUserNotification = () => {
|
||||||
.patchUserNotification(workspaceSlug.toString(), notificationId, {
|
.patchUserNotification(workspaceSlug.toString(), notificationId, {
|
||||||
snoozed_till: null,
|
snoozed_till: null,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.finally(() => {
|
||||||
notificationsMutate();
|
notificationsMutate();
|
||||||
});
|
});
|
||||||
} else
|
} else {
|
||||||
|
notificationsMutate(
|
||||||
|
(prevData) => prevData?.filter((prev) => prev.id !== notificationId) || [],
|
||||||
|
false
|
||||||
|
);
|
||||||
await userNotificationServices
|
await userNotificationServices
|
||||||
.patchUserNotification(workspaceSlug.toString(), notificationId, {
|
.patchUserNotification(workspaceSlug.toString(), notificationId, {
|
||||||
snoozed_till: dateTime,
|
snoozed_till: dateTime,
|
||||||
})
|
})
|
||||||
.then(() => {
|
.catch(() => {
|
||||||
notificationsMutate(
|
new Error("Something went wrong");
|
||||||
(prevData) => prevData?.filter((prev) => prev.id !== notificationId) || []
|
})
|
||||||
);
|
.finally(() => {
|
||||||
|
notificationsMutate();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
@ -170,7 +182,7 @@ const useUserNotification = () => {
|
||||||
setSelectedTab,
|
setSelectedTab,
|
||||||
totalNotificationCount: notificationCount
|
totalNotificationCount: notificationCount
|
||||||
? notificationCount.created_issues +
|
? notificationCount.created_issues +
|
||||||
notificationCount.watching_notifications +
|
notificationCount.watching_issues +
|
||||||
notificationCount.my_issues
|
notificationCount.my_issues
|
||||||
: null,
|
: null,
|
||||||
notificationCount,
|
notificationCount,
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@
|
||||||
"@headlessui/react": "^1.7.3",
|
"@headlessui/react": "^1.7.3",
|
||||||
"@heroicons/react": "^2.0.12",
|
"@heroicons/react": "^2.0.12",
|
||||||
"@jitsu/nextjs": "^3.1.5",
|
"@jitsu/nextjs": "^3.1.5",
|
||||||
|
"@mui/icons-material": "^5.14.1",
|
||||||
|
"@mui/material": "^5.14.1",
|
||||||
"@nivo/bar": "0.80.0",
|
"@nivo/bar": "0.80.0",
|
||||||
"@nivo/calendar": "0.80.0",
|
"@nivo/calendar": "0.80.0",
|
||||||
"@nivo/core": "0.80.0",
|
"@nivo/core": "0.80.0",
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,9 @@ import {
|
||||||
} from "components/workspace";
|
} from "components/workspace";
|
||||||
import { TourRoot } from "components/onboarding";
|
import { TourRoot } from "components/onboarding";
|
||||||
// ui
|
// ui
|
||||||
import { Icon, PrimaryButton, ProductUpdatesModal } from "components/ui";
|
import { PrimaryButton, ProductUpdatesModal } from "components/ui";
|
||||||
|
// icons
|
||||||
|
import { BoltOutlined, GridViewOutlined } from "@mui/icons-material";
|
||||||
// images
|
// images
|
||||||
import emptyDashboard from "public/empty-state/dashboard.svg";
|
import emptyDashboard from "public/empty-state/dashboard.svg";
|
||||||
import githubBlackImage from "/public/logos/github-black.png";
|
import githubBlackImage from "/public/logos/github-black.png";
|
||||||
|
|
@ -70,7 +72,7 @@ const WorkspacePage: NextPage = () => {
|
||||||
<WorkspaceAuthorizationLayout
|
<WorkspaceAuthorizationLayout
|
||||||
left={
|
left={
|
||||||
<div className="flex items-center gap-2 pl-3">
|
<div className="flex items-center gap-2 pl-3">
|
||||||
<Icon iconName="grid_view" />
|
<GridViewOutlined fontSize="small" />
|
||||||
Dashboard
|
Dashboard
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
@ -80,7 +82,7 @@ const WorkspacePage: NextPage = () => {
|
||||||
onClick={() => setIsProductUpdatesModalOpen(true)}
|
onClick={() => setIsProductUpdatesModalOpen(true)}
|
||||||
className="flex items-center gap-1.5 bg-custom-background-80 text-xs font-medium py-1.5 px-3 rounded"
|
className="flex items-center gap-1.5 bg-custom-background-80 text-xs font-medium py-1.5 px-3 rounded"
|
||||||
>
|
>
|
||||||
<Icon iconName="bolt" className="!text-base -my-1" />
|
<BoltOutlined fontSize="small" className="-my-1" />
|
||||||
What{"'"}s New?
|
What{"'"}s New?
|
||||||
</button>
|
</button>
|
||||||
<Link href="https://github.com/makeplane/plane" target="_blank" rel="noopener noreferrer">
|
<Link href="https://github.com/makeplane/plane" target="_blank" rel="noopener noreferrer">
|
||||||
|
|
|
||||||
|
|
@ -44,28 +44,18 @@ const BillingSettings: NextPage = () => {
|
||||||
<section className="space-y-8">
|
<section className="space-y-8">
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-2xl font-semibold leading-6">Billing & Plans</h3>
|
<h3 className="text-2xl font-semibold leading-6">Billing & Plans</h3>
|
||||||
<p className="mt-4 text-sm text-custom-text-200">[Free launch preview] plan Pro</p>
|
<p className="mt-4 text-sm text-custom-text-200">Free launch preview</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-8 md:w-2/3">
|
<div className="space-y-8 md:w-2/3">
|
||||||
<div>
|
|
||||||
<div className="w-80 rounded-md border border-custom-border-200 bg-custom-background-100 p-4 text-center">
|
|
||||||
<h4 className="text-md mb-1 leading-6">Payment due</h4>
|
|
||||||
<h2 className="text-3xl font-extrabold">--</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<h4 className="text-md mb-1 leading-6">Current plan</h4>
|
<h4 className="text-md mb-1 leading-6">Current plan</h4>
|
||||||
<p className="mb-3 text-sm text-custom-text-200">
|
<p className="mb-3 text-sm text-custom-text-200">
|
||||||
You are currently using the free plan
|
You are currently using the free plan
|
||||||
</p>
|
</p>
|
||||||
<a href="https://plane.so/pricing" target="_blank" rel="noreferrer">
|
<a href="https://plane.so/pricing" target="_blank" rel="noreferrer">
|
||||||
<SecondaryButton outline>View Plans and Upgrade</SecondaryButton>
|
<SecondaryButton outline>View Plans</SecondaryButton>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<h4 className="text-md mb-1 leading-6">Billing history</h4>
|
|
||||||
<p className="mb-3 text-sm text-custom-text-200">There are no invoices to display</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,10 @@ const WorkspaceSettings: NextPage = () => {
|
||||||
{isImageUploading ? "Uploading..." : "Upload"}
|
{isImageUploading ? "Uploading..." : "Upload"}
|
||||||
</SecondaryButton>
|
</SecondaryButton>
|
||||||
{activeWorkspace.logo && activeWorkspace.logo !== "" && (
|
{activeWorkspace.logo && activeWorkspace.logo !== "" && (
|
||||||
<DangerButton onClick={() => handleDelete(activeWorkspace.logo)}>
|
<DangerButton
|
||||||
|
onClick={() => handleDelete(activeWorkspace.logo)}
|
||||||
|
loading={isImageRemoving}
|
||||||
|
>
|
||||||
{isImageRemoving ? "Removing..." : "Remove"}
|
{isImageRemoving ? "Removing..." : "Remove"}
|
||||||
</DangerButton>
|
</DangerButton>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -145,11 +145,11 @@ const HomePage: NextPage = () => {
|
||||||
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
|
<h1 className="text-center text-2xl sm:text-2.5xl font-semibold text-custom-text-100">
|
||||||
Sign in to Plane
|
Sign in to Plane
|
||||||
</h1>
|
</h1>
|
||||||
<div className="flex flex-col divide-y divide-custom-border-200 sm:w-[360px] mx-auto">
|
<div className="flex flex-col divide-y divide-custom-border-200">
|
||||||
<div className="pb-7">
|
<div className="pb-7">
|
||||||
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
|
<EmailCodeForm handleSignIn={handleEmailCodeSignIn} />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center justify-center gap-4 pt-7 overflow-hidden">
|
<div className="flex flex-col items-center justify-center gap-4 pt-7 sm:w-[360px] mx-auto overflow-hidden">
|
||||||
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
|
<GoogleLoginButton handleSignIn={handleGoogleSignIn} />
|
||||||
<GithubLoginButton handleSignIn={handleGitHubSignIn} />
|
<GithubLoginButton handleSignIn={handleGitHubSignIn} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -105,12 +105,13 @@ const OnBoard: NextPage = () => {
|
||||||
{user?.email}
|
{user?.email}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{invitations && invitations.length > 0 ? (
|
{invitations ? (
|
||||||
|
invitations.length > 0 ? (
|
||||||
<div className="relative flex justify-center sm:justify-start sm:items-center h-full px-8 pb-8 sm:p-0 sm:pr-[8.33%] sm:w-10/12 md:w-9/12 lg:w-4/5">
|
<div className="relative flex justify-center sm:justify-start sm:items-center h-full px-8 pb-8 sm:p-0 sm:pr-[8.33%] sm:w-10/12 md:w-9/12 lg:w-4/5">
|
||||||
<div className="w-full space-y-10">
|
<div className="w-full space-y-10">
|
||||||
<h5 className="text-lg">We see that someone has invited you to</h5>
|
<h5 className="text-lg">We see that someone has invited you to</h5>
|
||||||
<h4 className="text-2xl font-semibold">Join a workspace</h4>
|
<h4 className="text-2xl font-semibold">Join a workspace</h4>
|
||||||
<div className="md:w-3/5 space-y-4">
|
<div className="max-h-[37vh] md:w-3/5 space-y-4 overflow-y-auto">
|
||||||
{invitations.map((invitation) => {
|
{invitations.map((invitation) => {
|
||||||
const isSelected = invitationsRespond.includes(invitation.id);
|
const isSelected = invitationsRespond.includes(invitation.id);
|
||||||
|
|
||||||
|
|
@ -189,7 +190,8 @@ const OnBoard: NextPage = () => {
|
||||||
onClick={() => router.push("/")}
|
onClick={() => router.push("/")}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</DefaultLayout>
|
</DefaultLayout>
|
||||||
</UserAuthorizationLayout>
|
</UserAuthorizationLayout>
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ class UserNotificationsServices extends APIService {
|
||||||
async getUnreadNotificationsCount(workspaceSlug: string): Promise<{
|
async getUnreadNotificationsCount(workspaceSlug: string): Promise<{
|
||||||
created_issues: number;
|
created_issues: number;
|
||||||
my_issues: number;
|
my_issues: number;
|
||||||
watching_notifications: number;
|
watching_issues: number;
|
||||||
}> {
|
}> {
|
||||||
return this.get(`/api/workspaces/${workspaceSlug}/users/notifications/unread/`)
|
return this.get(`/api/workspaces/${workspaceSlug}/users/notifications/unread/`)
|
||||||
.then((response) => response?.data)
|
.then((response) => response?.data)
|
||||||
|
|
|
||||||
|
|
@ -295,3 +295,7 @@ body {
|
||||||
:-ms-input-placeholder {
|
:-ms-input-placeholder {
|
||||||
color: rgb(var(--color-text-400));
|
color: rgb(var(--color-text-400));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bp4-overlay-content {
|
||||||
|
z-index: 555 !important;
|
||||||
|
}
|
||||||
|
|
|
||||||
157
yarn.lock
157
yarn.lock
|
|
@ -899,6 +899,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.11"
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.22.5", "@babel/runtime@^7.22.6":
|
||||||
|
version "7.22.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438"
|
||||||
|
integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.11"
|
||||||
|
|
||||||
"@babel/template@^7.18.10", "@babel/template@^7.20.7":
|
"@babel/template@^7.18.10", "@babel/template@^7.20.7":
|
||||||
version "7.20.7"
|
version "7.20.7"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
|
||||||
|
|
@ -1005,6 +1012,17 @@
|
||||||
"@emotion/weak-memoize" "^0.3.0"
|
"@emotion/weak-memoize" "^0.3.0"
|
||||||
stylis "4.1.4"
|
stylis "4.1.4"
|
||||||
|
|
||||||
|
"@emotion/cache@^11.11.0":
|
||||||
|
version "11.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/cache/-/cache-11.11.0.tgz#809b33ee6b1cb1a625fef7a45bc568ccd9b8f3ff"
|
||||||
|
integrity sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==
|
||||||
|
dependencies:
|
||||||
|
"@emotion/memoize" "^0.8.1"
|
||||||
|
"@emotion/sheet" "^1.2.2"
|
||||||
|
"@emotion/utils" "^1.2.1"
|
||||||
|
"@emotion/weak-memoize" "^0.3.1"
|
||||||
|
stylis "4.2.0"
|
||||||
|
|
||||||
"@emotion/css@^11.10.6":
|
"@emotion/css@^11.10.6":
|
||||||
version "11.10.8"
|
version "11.10.8"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.10.8.tgz#9dec9996ad9a1cc28ec8d26b1b27ab0b8f6fb053"
|
resolved "https://registry.yarnpkg.com/@emotion/css/-/css-11.10.8.tgz#9dec9996ad9a1cc28ec8d26b1b27ab0b8f6fb053"
|
||||||
|
|
@ -1028,11 +1046,23 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@emotion/memoize" "^0.8.0"
|
"@emotion/memoize" "^0.8.0"
|
||||||
|
|
||||||
|
"@emotion/is-prop-valid@^1.2.1":
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz#23116cf1ed18bfeac910ec6436561ecb1a3885cc"
|
||||||
|
integrity sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==
|
||||||
|
dependencies:
|
||||||
|
"@emotion/memoize" "^0.8.1"
|
||||||
|
|
||||||
"@emotion/memoize@^0.8.0":
|
"@emotion/memoize@^0.8.0":
|
||||||
version "0.8.0"
|
version "0.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
|
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.0.tgz#f580f9beb67176fa57aae70b08ed510e1b18980f"
|
||||||
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
|
integrity sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==
|
||||||
|
|
||||||
|
"@emotion/memoize@^0.8.1":
|
||||||
|
version "0.8.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/memoize/-/memoize-0.8.1.tgz#c1ddb040429c6d21d38cc945fe75c818cfb68e17"
|
||||||
|
integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
|
||||||
|
|
||||||
"@emotion/react@^11.10.6":
|
"@emotion/react@^11.10.6":
|
||||||
version "11.10.8"
|
version "11.10.8"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.8.tgz#02e274ecb45e03ab9d7a8eb9f0f0c064613eaf7b"
|
resolved "https://registry.yarnpkg.com/@emotion/react/-/react-11.10.8.tgz#02e274ecb45e03ab9d7a8eb9f0f0c064613eaf7b"
|
||||||
|
|
@ -1063,6 +1093,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c"
|
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.1.tgz#0767e0305230e894897cadb6c8df2c51e61a6c2c"
|
||||||
integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==
|
integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==
|
||||||
|
|
||||||
|
"@emotion/sheet@^1.2.2":
|
||||||
|
version "1.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/sheet/-/sheet-1.2.2.tgz#d58e788ee27267a14342303e1abb3d508b6d0fec"
|
||||||
|
integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
|
||||||
|
|
||||||
"@emotion/styled@^11.10.6":
|
"@emotion/styled@^11.10.6":
|
||||||
version "11.10.8"
|
version "11.10.8"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.8.tgz#a3fd68efd90bd7e8a06b82b95adec643d386fa69"
|
resolved "https://registry.yarnpkg.com/@emotion/styled/-/styled-11.10.8.tgz#a3fd68efd90bd7e8a06b82b95adec643d386fa69"
|
||||||
|
|
@ -1090,11 +1125,21 @@
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
|
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.0.tgz#9716eaccbc6b5ded2ea5a90d65562609aab0f561"
|
||||||
integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
|
integrity sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==
|
||||||
|
|
||||||
|
"@emotion/utils@^1.2.1":
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/utils/-/utils-1.2.1.tgz#bbab58465738d31ae4cb3dbb6fc00a5991f755e4"
|
||||||
|
integrity sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==
|
||||||
|
|
||||||
"@emotion/weak-memoize@^0.3.0":
|
"@emotion/weak-memoize@^0.3.0":
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
|
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz#ea89004119dc42db2e1dba0f97d553f7372f6fcb"
|
||||||
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
integrity sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==
|
||||||
|
|
||||||
|
"@emotion/weak-memoize@^0.3.1":
|
||||||
|
version "0.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz#d0fce5d07b0620caa282b5131c297bb60f9d87e6"
|
||||||
|
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
|
||||||
|
|
||||||
"@eslint-community/eslint-utils@^4.2.0":
|
"@eslint-community/eslint-utils@^4.2.0":
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
||||||
|
|
@ -1333,11 +1378,37 @@
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^18.2.0"
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/base@5.0.0-beta.8":
|
||||||
|
version "5.0.0-beta.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-beta.8.tgz#a0a9531ae9147be92d17e4f0e3b9accc57916841"
|
||||||
|
integrity sha512-b4vVjMZx5KzzEMf4arXKoeV5ZegAMOoPwoy1vfUBwhvXc2QtaaAyBp50U7OA2L06Leubc1A+lEp3eqwZoFn87g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.22.6"
|
||||||
|
"@emotion/is-prop-valid" "^1.2.1"
|
||||||
|
"@mui/types" "^7.2.4"
|
||||||
|
"@mui/utils" "^5.14.1"
|
||||||
|
"@popperjs/core" "^2.11.8"
|
||||||
|
clsx "^1.2.1"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^18.2.0"
|
||||||
|
|
||||||
"@mui/core-downloads-tracker@^5.12.3":
|
"@mui/core-downloads-tracker@^5.12.3":
|
||||||
version "5.12.3"
|
version "5.12.3"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.3.tgz#3dffe62dccc065ddd7338e97d7be4b917004287e"
|
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.12.3.tgz#3dffe62dccc065ddd7338e97d7be4b917004287e"
|
||||||
integrity sha512-yiJZ+knaknPHuRKhRk4L6XiwppwkAahVal3LuYpvBH7GkA2g+D9WLEXOEnNYtVFUggyKf6fWGLGnx0iqzkU5YA==
|
integrity sha512-yiJZ+knaknPHuRKhRk4L6XiwppwkAahVal3LuYpvBH7GkA2g+D9WLEXOEnNYtVFUggyKf6fWGLGnx0iqzkU5YA==
|
||||||
|
|
||||||
|
"@mui/core-downloads-tracker@^5.14.1":
|
||||||
|
version "5.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.1.tgz#af156cb3e15b202f5c09f66e7d8b71ca86aef525"
|
||||||
|
integrity sha512-mIa1WmDmNr1LoupV1Rbxt9bTFKMbIn10RHG1bnZ/FJCkAYpuU/D4n+R+ttiycgcZNngU++zyh/OQeJblzbQPzg==
|
||||||
|
|
||||||
|
"@mui/icons-material@^5.14.1":
|
||||||
|
version "5.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/icons-material/-/icons-material-5.14.1.tgz#2f145c15047a0c7f01353ce620cb88276dadba9e"
|
||||||
|
integrity sha512-xV/f26muQqtWzerzOIdGPrXoxp/OKaE2G2Wp9gnmG47mHua5Slup/tMc3fA4ZYUreGGrK6+tT81TEvt1Wsng8Q==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.22.6"
|
||||||
|
|
||||||
"@mui/material@^5.12.1":
|
"@mui/material@^5.12.1":
|
||||||
version "5.12.3"
|
version "5.12.3"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.3.tgz#398c1b123fb065763558bc1f9fc47d1f8cb87d0c"
|
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.12.3.tgz#398c1b123fb065763558bc1f9fc47d1f8cb87d0c"
|
||||||
|
|
@ -1356,6 +1427,24 @@
|
||||||
react-is "^18.2.0"
|
react-is "^18.2.0"
|
||||||
react-transition-group "^4.4.5"
|
react-transition-group "^4.4.5"
|
||||||
|
|
||||||
|
"@mui/material@^5.14.1":
|
||||||
|
version "5.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/material/-/material-5.14.1.tgz#2711e4ca5c9bdc67b916d01faee650a7a5260bb8"
|
||||||
|
integrity sha512-WtsgYuageTunLfxH3Ri+o1RuQTFImtRHxMcVNyD0Hhd2/znjW6KODNz0XfjvLRnNCAynBxZNiflcoIBW40h9PQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.22.6"
|
||||||
|
"@mui/base" "5.0.0-beta.8"
|
||||||
|
"@mui/core-downloads-tracker" "^5.14.1"
|
||||||
|
"@mui/system" "^5.14.1"
|
||||||
|
"@mui/types" "^7.2.4"
|
||||||
|
"@mui/utils" "^5.14.1"
|
||||||
|
"@types/react-transition-group" "^4.4.6"
|
||||||
|
clsx "^1.2.1"
|
||||||
|
csstype "^3.1.2"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^18.2.0"
|
||||||
|
react-transition-group "^4.4.5"
|
||||||
|
|
||||||
"@mui/private-theming@^5.12.3":
|
"@mui/private-theming@^5.12.3":
|
||||||
version "5.12.3"
|
version "5.12.3"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.12.3.tgz#f5e4704e25d9d91b906561cae573cda8f3801e10"
|
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.12.3.tgz#f5e4704e25d9d91b906561cae573cda8f3801e10"
|
||||||
|
|
@ -1365,6 +1454,15 @@
|
||||||
"@mui/utils" "^5.12.3"
|
"@mui/utils" "^5.12.3"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
|
"@mui/private-theming@^5.13.7":
|
||||||
|
version "5.13.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/private-theming/-/private-theming-5.13.7.tgz#2f8ef5da066f3c6c6423bd4260d003a28d10b099"
|
||||||
|
integrity sha512-qbSr+udcij5F9dKhGX7fEdx2drXchq7htLNr2Qg2Ma+WJ6q0ERlEqGSBiPiVDJkptcjeVL4DGmcf1wl5+vD4EA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.22.5"
|
||||||
|
"@mui/utils" "^5.13.7"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/styled-engine@^5.12.3":
|
"@mui/styled-engine@^5.12.3":
|
||||||
version "5.12.3"
|
version "5.12.3"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.12.3.tgz#3307643d52c81947a624cdd0437536cc8109c4f0"
|
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.12.3.tgz#3307643d52c81947a624cdd0437536cc8109c4f0"
|
||||||
|
|
@ -1375,6 +1473,16 @@
|
||||||
csstype "^3.1.2"
|
csstype "^3.1.2"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
|
"@mui/styled-engine@^5.13.2":
|
||||||
|
version "5.13.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/styled-engine/-/styled-engine-5.13.2.tgz#c87bd61c0ab8086d34828b6defe97c02bcd642ef"
|
||||||
|
integrity sha512-VCYCU6xVtXOrIN8lcbuPmoG+u7FYuOERG++fpY74hPpEWkyFQG97F+/XfTQVYzlR2m7nPjnwVUgATcTCMEaMvw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.21.0"
|
||||||
|
"@emotion/cache" "^11.11.0"
|
||||||
|
csstype "^3.1.2"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/system@^5.12.3":
|
"@mui/system@^5.12.3":
|
||||||
version "5.12.3"
|
version "5.12.3"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.3.tgz#306b3cdffa3046067640219c1e5dd7e3dae38ff9"
|
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.12.3.tgz#306b3cdffa3046067640219c1e5dd7e3dae38ff9"
|
||||||
|
|
@ -1389,6 +1497,20 @@
|
||||||
csstype "^3.1.2"
|
csstype "^3.1.2"
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
|
"@mui/system@^5.14.1":
|
||||||
|
version "5.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/system/-/system-5.14.1.tgz#ec8ae69f63963b5916dad4bca2f8a86a001a2392"
|
||||||
|
integrity sha512-u+xlsU34Jdkgx1CxmBnIC4Y08uPdVX5iEd3S/1dggDFtOGp+Lj8xmKRJAQ8PJOOJLOh8pDwaZx4AwXikL4l1QA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.22.6"
|
||||||
|
"@mui/private-theming" "^5.13.7"
|
||||||
|
"@mui/styled-engine" "^5.13.2"
|
||||||
|
"@mui/types" "^7.2.4"
|
||||||
|
"@mui/utils" "^5.14.1"
|
||||||
|
clsx "^1.2.1"
|
||||||
|
csstype "^3.1.2"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
"@mui/types@^7.2.4":
|
"@mui/types@^7.2.4":
|
||||||
version "7.2.4"
|
version "7.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328"
|
resolved "https://registry.yarnpkg.com/@mui/types/-/types-7.2.4.tgz#b6fade19323b754c5c6de679a38f068fd50b9328"
|
||||||
|
|
@ -1405,6 +1527,17 @@
|
||||||
prop-types "^15.8.1"
|
prop-types "^15.8.1"
|
||||||
react-is "^18.2.0"
|
react-is "^18.2.0"
|
||||||
|
|
||||||
|
"@mui/utils@^5.13.7", "@mui/utils@^5.14.1":
|
||||||
|
version "5.14.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@mui/utils/-/utils-5.14.1.tgz#29696371016552a6eb3af975bc7af429ec88b29a"
|
||||||
|
integrity sha512-39KHKK2JeqRmuUcLDLwM+c2XfVC136C5/yUyQXmO2PVbOb2Bol4KxtkssEqCbTwg87PSCG3f1Tb0keRsK7cVGw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.22.6"
|
||||||
|
"@types/prop-types" "^15.7.5"
|
||||||
|
"@types/react-is" "^18.2.1"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
react-is "^18.2.0"
|
||||||
|
|
||||||
"@next/env@12.3.2":
|
"@next/env@12.3.2":
|
||||||
version "12.3.2"
|
version "12.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.2.tgz#fb819366771f5721e9438ca3a42ad18684f0949b"
|
resolved "https://registry.yarnpkg.com/@next/env/-/env-12.3.2.tgz#fb819366771f5721e9438ca3a42ad18684f0949b"
|
||||||
|
|
@ -1684,6 +1817,11 @@
|
||||||
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7"
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.7.tgz#ccab5c8f7dc557a52ca3288c10075c9ccd37fff7"
|
||||||
integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==
|
integrity sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==
|
||||||
|
|
||||||
|
"@popperjs/core@^2.11.8":
|
||||||
|
version "2.11.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f"
|
||||||
|
integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==
|
||||||
|
|
||||||
"@radix-ui/primitive@1.0.0":
|
"@radix-ui/primitive@1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253"
|
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253"
|
||||||
|
|
@ -3230,6 +3368,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "^17"
|
"@types/react" "^17"
|
||||||
|
|
||||||
|
"@types/react-is@^18.2.1":
|
||||||
|
version "18.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-is/-/react-is-18.2.1.tgz#61d01c2a6fc089a53520c0b66996d458fdc46863"
|
||||||
|
integrity sha512-wyUkmaaSZEzFZivD8F2ftSyAfk6L+DfFliVj/mYdOXbVjRcS87fQJLTnhk6dRZPuJjI+9g6RZJO4PNCngUrmyw==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react-redux@^7.1.20":
|
"@types/react-redux@^7.1.20":
|
||||||
version "7.1.25"
|
version "7.1.25"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.25.tgz#de841631205b24f9dfb4967dd4a7901e048f9a88"
|
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.25.tgz#de841631205b24f9dfb4967dd4a7901e048f9a88"
|
||||||
|
|
@ -3247,6 +3392,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/react" "*"
|
"@types/react" "*"
|
||||||
|
|
||||||
|
"@types/react-transition-group@^4.4.6":
|
||||||
|
version "4.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.6.tgz#18187bcda5281f8e10dfc48f0943e2fdf4f75e2e"
|
||||||
|
integrity sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==
|
||||||
|
dependencies:
|
||||||
|
"@types/react" "*"
|
||||||
|
|
||||||
"@types/react@*", "@types/react@^18.0.17":
|
"@types/react@*", "@types/react@^18.0.17":
|
||||||
version "18.2.3"
|
version "18.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.3.tgz#509ad6c4c77378e686f9bb6e0f8756936392f0e8"
|
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.3.tgz#509ad6c4c77378e686f9bb6e0f8756936392f0e8"
|
||||||
|
|
@ -7947,6 +8099,11 @@ stylis@4.1.4:
|
||||||
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b"
|
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.4.tgz#9cb60e7153d8ac6d02d773552bf51c7a0344535b"
|
||||||
integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ==
|
integrity sha512-USf5pszRYwuE6hg9by0OkKChkQYEXfkeTtm0xKw+jqQhwyjCVLdYyMBK7R+n7dhzsblAWJnGxju4vxq5eH20GQ==
|
||||||
|
|
||||||
|
stylis@4.2.0:
|
||||||
|
version "4.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.2.0.tgz#79daee0208964c8fe695a42fcffcac633a211a51"
|
||||||
|
integrity sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==
|
||||||
|
|
||||||
sucrase@^3.32.0:
|
sucrase@^3.32.0:
|
||||||
version "3.32.0"
|
version "3.32.0"
|
||||||
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.32.0.tgz#c4a95e0f1e18b6847127258a75cf360bc568d4a7"
|
resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.32.0.tgz#c4a95e0f1e18b6847127258a75cf360bc568d4a7"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue