Merge branch 'preview' of gurusainath:makeplane/plane into revamp-estimates

This commit is contained in:
guru_sainath 2024-05-25 09:11:46 +05:30
commit a3c9d5639e
29 changed files with 197 additions and 112 deletions

View File

@ -19,14 +19,14 @@ const InstanceAIPage = observer(() => {
return ( return (
<> <>
<PageHeader title="Artificial Intelligence - God Mode" /> <PageHeader title="Artificial Intelligence - God Mode" />
<div className="relative container mx-auto w-full h-full p-8 py-4 space-y-6 flex flex-col"> <div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 pb-3 space-y-1 flex-shrink-0"> <div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">AI features for all your workspaces</div> <div className="text-xl font-medium text-custom-text-100">AI features for all your workspaces</div>
<div className="text-sm font-normal text-custom-text-300"> <div className="text-sm font-normal text-custom-text-300">
Configure your AI API credentials so Plane AI features are turned on for all your workspaces. Configure your AI API credentials so Plane AI features are turned on for all your workspaces.
</div> </div>
</div> </div>
<div className="flex-grow overflow-hidden overflow-y-auto"> <div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? ( {formattedConfig ? (
<InstanceAIForm config={formattedConfig} /> <InstanceAIForm config={formattedConfig} />
) : ( ) : (

View File

@ -64,8 +64,8 @@ const InstanceGithubAuthenticationPage = observer(() => {
return ( return (
<> <>
<PageHeader title="Authentication - God Mode" /> <PageHeader title="Authentication - God Mode" />
<div className="relative container mx-auto w-full h-full p-8 py-4 space-y-6 flex flex-col"> <div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 pb-3 space-y-1 flex-shrink-0"> <div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<AuthenticationMethodCard <AuthenticationMethodCard
name="Github" name="Github"
description="Allow members to login or sign up to plane with their Github accounts." description="Allow members to login or sign up to plane with their Github accounts."
@ -93,7 +93,7 @@ const InstanceGithubAuthenticationPage = observer(() => {
withBorder={false} withBorder={false}
/> />
</div> </div>
<div className="flex-grow overflow-hidden overflow-y-auto"> <div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md p-4">
{formattedConfig ? ( {formattedConfig ? (
<InstanceGithubConfigForm config={formattedConfig} /> <InstanceGithubConfigForm config={formattedConfig} />
) : ( ) : (

View File

@ -58,8 +58,8 @@ const InstanceGoogleAuthenticationPage = observer(() => {
return ( return (
<> <>
<PageHeader title="Authentication - God Mode" /> <PageHeader title="Authentication - God Mode" />
<div className="relative container mx-auto w-full h-full p-8 py-4 space-y-6 flex flex-col"> <div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 pb-3 space-y-1 flex-shrink-0"> <div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<AuthenticationMethodCard <AuthenticationMethodCard
name="Google" name="Google"
description="Allow members to login or sign up to plane with their Google description="Allow members to login or sign up to plane with their Google
@ -81,7 +81,7 @@ const InstanceGoogleAuthenticationPage = observer(() => {
withBorder={false} withBorder={false}
/> />
</div> </div>
<div className="flex-grow overflow-hidden overflow-y-auto"> <div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md p-4">
{formattedConfig ? ( {formattedConfig ? (
<InstanceGoogleConfigForm config={formattedConfig} /> <InstanceGoogleConfigForm config={formattedConfig} />
) : ( ) : (

View File

@ -119,14 +119,14 @@ const InstanceAuthenticationPage = observer(() => {
return ( return (
<> <>
<PageHeader title="Authentication - God Mode" /> <PageHeader title="Authentication - God Mode" />
<div className="relative container mx-auto w-full h-full p-8 py-4 space-y-6 flex flex-col"> <div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 pb-3 space-y-1 flex-shrink-0"> <div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Manage authentication for your instance</div> <div className="text-xl font-medium text-custom-text-100">Manage authentication for your instance</div>
<div className="text-sm font-normal text-custom-text-300"> <div className="text-sm font-normal text-custom-text-300">
Configure authentication modes for your team and restrict sign ups to be invite only. Configure authentication modes for your team and restrict sign ups to be invite only.
</div> </div>
</div> </div>
<div className="flex-grow overflow-hidden overflow-y-auto"> <div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? ( {formattedConfig ? (
<div className="space-y-3"> <div className="space-y-3">
<div className="text-lg font-medium">Authentication modes</div> <div className="text-lg font-medium">Authentication modes</div>

View File

@ -19,8 +19,8 @@ const InstanceEmailPage = observer(() => {
return ( return (
<> <>
<PageHeader title="Email - God Mode" /> <PageHeader title="Email - God Mode" />
<div className="relative container mx-auto w-full h-full p-8 py-4 space-y-6 flex flex-col"> <div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 pb-3 space-y-1 flex-shrink-0"> <div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Secure emails from your own instance</div> <div className="text-xl font-medium text-custom-text-100">Secure emails from your own instance</div>
<div className="text-sm font-normal text-custom-text-300"> <div className="text-sm font-normal text-custom-text-300">
Plane can send useful emails to you and your users from your own instance without talking to the Internet. Plane can send useful emails to you and your users from your own instance without talking to the Internet.
@ -30,7 +30,7 @@ const InstanceEmailPage = observer(() => {
</div> </div>
</div> </div>
</div> </div>
<div className="flex-grow overflow-hidden overflow-y-auto"> <div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? ( {formattedConfig ? (
<InstanceEmailForm config={formattedConfig} /> <InstanceEmailForm config={formattedConfig} />
) : ( ) : (

View File

@ -51,7 +51,7 @@ export const SendTestEmailModal: FC<Props> = (props) => {
setSendEmailStep(ESendEmailSteps.SUCCESS); setSendEmailStep(ESendEmailSteps.SUCCESS);
}) })
.catch((error) => { .catch((error) => {
setError(error?.message || "Failed to send email"); setError(error?.error || "Failed to send email");
setSendEmailStep(ESendEmailSteps.FAILED); setSendEmailStep(ESendEmailSteps.FAILED);
}) })
.finally(() => { .finally(() => {

View File

@ -10,15 +10,15 @@ function GeneralPage() {
console.log("instance", instance); console.log("instance", instance);
return ( return (
<> <>
<div className="relative container mx-auto w-full h-full p-8 py-4 space-y-6 flex flex-col"> <div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 pb-3 space-y-1 flex-shrink-0"> <div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">General settings</div> <div className="text-xl font-medium text-custom-text-100">General settings</div>
<div className="text-sm font-normal text-custom-text-300"> <div className="text-sm font-normal text-custom-text-300">
Change the name of your instance and instance admin e-mail addresses. Enable or disable telemetry in your Change the name of your instance and instance admin e-mail addresses. Enable or disable telemetry in your
instance. instance.
</div> </div>
</div> </div>
<div className="flex-grow overflow-hidden overflow-y-auto"> <div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{instance && instanceAdmins && ( {instance && instanceAdmins && (
<GeneralConfigurationForm instance={instance} instanceAdmins={instanceAdmins} /> <GeneralConfigurationForm instance={instance} instanceAdmins={instanceAdmins} />
)} )}

View File

@ -332,42 +332,90 @@ body {
} }
/* scrollbar style */ /* scrollbar style */
::-webkit-scrollbar { @-moz-document url-prefix() {
display: none; * {
scrollbar-width: none;
}
.vertical-scrollbar,
.horizontal-scrollbar {
scrollbar-width: initial;
scrollbar-color: rgba(96, 100, 108, 0.1) transparent;
}
.vertical-scrollbar:hover,
.horizontal-scrollbar:hover {
scrollbar-color: rgba(96, 100, 108, 0.25) transparent;
}
.vertical-scrollbar:active,
.horizontal-scrollbar:active {
scrollbar-color: rgba(96, 100, 108, 0.7) transparent;
}
} }
.horizontal-scroll-enable { .vertical-scrollbar {
overflow-x: scroll; overflow-y: auto;
} }
.horizontal-scrollbar {
.horizontal-scroll-enable::-webkit-scrollbar { overflow-x: auto;
}
.vertical-scrollbar::-webkit-scrollbar,
.horizontal-scrollbar::-webkit-scrollbar {
display: block; display: block;
height: 7px; }
width: 0; .vertical-scrollbar::-webkit-scrollbar-track,
.horizontal-scrollbar::-webkit-scrollbar-track {
background-color: transparent;
border-radius: 9999px;
}
.vertical-scrollbar::-webkit-scrollbar-thumb,
.horizontal-scrollbar::-webkit-scrollbar-thumb {
background-clip: padding-box;
background-color: rgba(96, 100, 108, 0.1);
border-radius: 9999px;
}
.vertical-scrollbar:hover::-webkit-scrollbar-thumb,
.horizontal-scrollbar:hover::-webkit-scrollbar-thumb {
background-color: rgba(96, 100, 108, 0.25);
}
.vertical-scrollbar::-webkit-scrollbar-thumb:hover,
.horizontal-scrollbar::-webkit-scrollbar-thumb:hover {
background-color: rgba(96, 100, 108, 0.5);
}
.vertical-scrollbar::-webkit-scrollbar-thumb:active,
.horizontal-scrollbar::-webkit-scrollbar-thumb:active {
background-color: rgba(96, 100, 108, 0.7);
}
.vertical-scrollbar::-webkit-scrollbar-corner,
.horizontal-scrollbar::-webkit-scrollbar-corner {
background-color: transparent;
}
.vertical-scrollbar-margin-top-md::-webkit-scrollbar-track {
margin-top: 44px;
} }
.horizontal-scroll-enable::-webkit-scrollbar-track { /* scrollbar sm size */
height: 7px; .scrollbar-sm::-webkit-scrollbar {
background-color: rgba(var(--color-background-100)); height: 12px;
width: 12px;
} }
.scrollbar-sm::-webkit-scrollbar-thumb {
.horizontal-scroll-enable::-webkit-scrollbar-thumb { border: 3px solid rgba(0, 0, 0, 0);
border-radius: 5px;
background-color: rgba(var(--color-scrollbar));
} }
/* scrollbar md size */
.vertical-scroll-enable::-webkit-scrollbar { .scrollbar-md::-webkit-scrollbar {
display: block; height: 14px;
width: 5px; width: 14px;
} }
.scrollbar-md::-webkit-scrollbar-thumb {
.vertical-scroll-enable::-webkit-scrollbar-track { border: 3px solid rgba(0, 0, 0, 0);
width: 5px;
} }
/* scrollbar lg size */
.vertical-scroll-enable::-webkit-scrollbar-thumb { .scrollbar-lg::-webkit-scrollbar {
border-radius: 5px; height: 16px;
background-color: rgba(var(--color-background-90)); width: 16px;
}
.scrollbar-lg::-webkit-scrollbar-thumb {
border: 4px solid rgba(0, 0, 0, 0);
} }
/* end scrollbar style */ /* end scrollbar style */

View File

@ -19,14 +19,14 @@ const InstanceImagePage = observer(() => {
return ( return (
<> <>
<PageHeader title="Image - God Mode" /> <PageHeader title="Image - God Mode" />
<div className="relative container mx-auto w-full h-full p-8 py-4 space-y-6 flex flex-col"> <div className="relative container mx-auto w-full h-full p-4 py-4 space-y-6 flex flex-col">
<div className="border-b border-custom-border-100 pb-3 space-y-1 flex-shrink-0"> <div className="border-b border-custom-border-100 mx-4 py-4 space-y-1 flex-shrink-0">
<div className="text-xl font-medium text-custom-text-100">Third-party image libraries</div> <div className="text-xl font-medium text-custom-text-100">Third-party image libraries</div>
<div className="text-sm font-normal text-custom-text-300"> <div className="text-sm font-normal text-custom-text-300">
Let your users search and choose images from third-party libraries Let your users search and choose images from third-party libraries
</div> </div>
</div> </div>
<div className="flex-grow overflow-hidden overflow-y-auto"> <div className="flex-grow overflow-hidden overflow-y-scroll vertical-scrollbar scrollbar-md px-4">
{formattedConfig ? ( {formattedConfig ? (
<InstanceImageConfigForm config={formattedConfig} /> <InstanceImageConfigForm config={formattedConfig} />
) : ( ) : (

View File

@ -38,7 +38,7 @@ export const HelpSection: FC = observer(() => {
// refs // refs
const helpOptionsRef = useRef<HTMLDivElement | null>(null); const helpOptionsRef = useRef<HTMLDivElement | null>(null);
const redirectionLink = encodeURI(WEB_BASE_URL + "/create-workspace"); const redirectionLink = encodeURI(WEB_BASE_URL + "/");
return ( return (
<div <div

View File

@ -56,7 +56,7 @@ export const SidebarMenu = observer(() => {
}; };
return ( return (
<div className="flex h-full w-full flex-col gap-2.5 overflow-y-auto px-4 py-4"> <div className="flex h-full w-full flex-col gap-2.5 overflow-y-scroll vertical-scrollbar scrollbar-sm px-4 py-4">
{INSTANCE_ADMIN_LINKS.map((item, index) => { {INSTANCE_ADMIN_LINKS.map((item, index) => {
const isActive = item.href === pathName || pathName.includes(item.href); const isActive = item.href === pathName || pathName.includes(item.href);
return ( return (

View File

@ -158,6 +158,7 @@ export const InstanceSetupForm: FC = (props) => {
onError={() => setIsSubmitting(false)} onError={() => setIsSubmitting(false)}
> >
<input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} /> <input type="hidden" name="csrfmiddlewaretoken" value={csrfToken} />
<input type="hidden" name="is_telemetry_enabled" value={formData.is_telemetry_enabled ? "True" : "False"} />
<div className="flex flex-col sm:flex-row items-center gap-4"> <div className="flex flex-col sm:flex-row items-center gap-4">
<div className="w-full space-y-1"> <div className="w-full space-y-1">
@ -319,8 +320,6 @@ export const InstanceSetupForm: FC = (props) => {
<div> <div>
<Checkbox <Checkbox
id="is_telemetry_enabled" id="is_telemetry_enabled"
name="is_telemetry_enabled"
value={formData.is_telemetry_enabled ? "True" : "False"}
onChange={() => handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)} onChange={() => handleFormChange("is_telemetry_enabled", !formData.is_telemetry_enabled)}
checked={formData.is_telemetry_enabled} checked={formData.is_telemetry_enabled}
/> />

View File

@ -7,9 +7,9 @@ import { useTheme as nextUseTheme } from "next-themes";
// ui // ui
import { Button, getButtonStyling } from "@plane/ui"; import { Button, getButtonStyling } from "@plane/ui";
// helpers // helpers
import { resolveGeneralTheme } from "helpers/common.helper"; import { WEB_BASE_URL, resolveGeneralTheme } from "helpers/common.helper";
// hooks // hooks
import { useInstance, useTheme } from "@/hooks/store"; import { useTheme } from "@/hooks/store";
// icons // icons
import TakeoffIconLight from "/public/logos/takeoff-icon-light.svg"; import TakeoffIconLight from "/public/logos/takeoff-icon-light.svg";
import TakeoffIconDark from "/public/logos/takeoff-icon-dark.svg"; import TakeoffIconDark from "/public/logos/takeoff-icon-dark.svg";
@ -17,11 +17,10 @@ import TakeoffIconDark from "/public/logos/takeoff-icon-dark.svg";
export const NewUserPopup: React.FC = observer(() => { export const NewUserPopup: React.FC = observer(() => {
// hooks // hooks
const { isNewUserPopup, toggleNewUserPopup } = useTheme(); const { isNewUserPopup, toggleNewUserPopup } = useTheme();
const { config } = useInstance();
// theme // theme
const { resolvedTheme } = nextUseTheme(); const { resolvedTheme } = nextUseTheme();
const redirectionLink = `${config?.app_base_url ? `${config?.app_base_url}/create-workspace` : `/god-mode/`}`; const redirectionLink = encodeURI(WEB_BASE_URL + "/create-workspace");
if (!isNewUserPopup) return <></>; if (!isNewUserPopup) return <></>;
return ( return (

View File

@ -17,6 +17,7 @@ AUTHENTICATION_ERROR_CODES = {
"INVALID_EMAIL_SIGN_UP": 5045, "INVALID_EMAIL_SIGN_UP": 5045,
"INVALID_EMAIL_MAGIC_SIGN_UP": 5050, "INVALID_EMAIL_MAGIC_SIGN_UP": 5050,
"MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED": 5055, "MAGIC_SIGN_UP_EMAIL_CODE_REQUIRED": 5055,
"EMAIL_PASSWORD_AUTHENTICATION_DISABLED": 5056,
# Sign In # Sign In
"USER_DOES_NOT_EXIST": 5060, "USER_DOES_NOT_EXIST": 5060,
"AUTHENTICATION_FAILED_SIGN_IN": 5065, "AUTHENTICATION_FAILED_SIGN_IN": 5065,

View File

@ -41,8 +41,10 @@ class EmailProvider(CredentialAdapter):
if ENABLE_EMAIL_PASSWORD == "0": if ENABLE_EMAIL_PASSWORD == "0":
raise AuthenticationException( raise AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["ENABLE_EMAIL_PASSWORD"], error_code=AUTHENTICATION_ERROR_CODES[
error_message="ENABLE_EMAIL_PASSWORD", "EMAIL_PASSWORD_AUTHENTICATION_DISABLED"
],
error_message="EMAIL_PASSWORD_AUTHENTICATION_DISABLED",
) )
def set_user_data(self): def set_user_data(self):

View File

@ -148,7 +148,7 @@ class InstanceEndpoint(BaseAPIView):
data["app_base_url"] = settings.APP_BASE_URL data["app_base_url"] = settings.APP_BASE_URL
instance_data = serializer.data instance_data = serializer.data
instance_data["workspaces_exist"] = Workspace.objects.count() > 1 instance_data["workspaces_exist"] = Workspace.objects.count() >= 1
response_data = {"config": data, "instance": instance_data} response_data = {"config": data, "instance": instance_data}
return Response(response_data, status=status.HTTP_200_OK) return Response(response_data, status=status.HTTP_200_OK)

View File

@ -68,7 +68,7 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id, nestingLevel: nestingLevel }); setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id, nestingLevel: nestingLevel });
const issue = issuesMap[issueId]; const issue = issuesMap[issueId];
const subIssuesCount = issue.sub_issues_count; const subIssuesCount = issue?.sub_issues_count ?? 0;
const { isMobile } = usePlatformOS(); const { isMobile } = usePlatformOS();

View File

@ -63,7 +63,7 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
const currentLayout = `${activeLayout} layout`; const currentLayout = `${activeLayout} layout`;
// derived values // derived values
const stateDetails = getStateById(issue.state_id); const stateDetails = getStateById(issue.state_id);
const subIssueCount = issue.sub_issues_count; const subIssueCount = issue?.sub_issues_count ?? 0;
const issueOperations = useMemo( const issueOperations = useMemo(
() => ({ () => ({

View File

@ -19,7 +19,7 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
// hooks // hooks
const { workspaceSlug } = useAppRouter(); const { workspaceSlug } = useAppRouter();
// derived values // derived values
const subIssueCount = issue.sub_issues_count; const subIssueCount = issue?.sub_issues_count ?? 0;
const redirectToIssueDetail = () => { const redirectToIssueDetail = () => {
router.push({ router.push({

View File

@ -203,7 +203,7 @@ const IssueRowDetails = observer((props: IssueRowDetailsProps) => {
}; };
const disableUserActions = !canEditProperties(issueDetail.project_id); const disableUserActions = !canEditProperties(issueDetail.project_id);
const subIssuesCount = issueDetail.sub_issues_count; const subIssuesCount = issueDetail?.sub_issues_count ?? 0;
return ( return (
<> <>

View File

@ -61,7 +61,7 @@ export const IssueListItem: React.FC<ISubIssues> = observer((props) => {
undefined; undefined;
const subIssueHelpers = subIssueHelpersByIssueId(parentIssueId); const subIssueHelpers = subIssueHelpersByIssueId(parentIssueId);
const subIssueCount = issue?.sub_issues_count || 0; const subIssueCount = issue?.sub_issues_count ?? 0;
const handleIssuePeekOverview = (issue: TIssue) => const handleIssuePeekOverview = (issue: TIssue) =>
workspaceSlug && workspaceSlug &&

View File

@ -59,9 +59,8 @@ const calculateShades = (hexValue: string): TShades => {
return shades as TShades; return shades as TShades;
}; };
export const applyTheme = (palette: string, isDarkPalette: boolean) => { export const applyTheme = (palette: string, isDarkPalette: boolean, dom: HTMLElement | null) => {
if (!palette) return; if (!palette) return;
const dom = document?.querySelector<HTMLElement>("[data-theme='custom']");
// palette: [bg, text, primary, sidebarBg, sidebarText] // palette: [bg, text, primary, sidebarBg, sidebarText]
const values: string[] = palette.split(","); const values: string[] = palette.split(",");
values.push(isDarkPalette ? "dark" : "light"); values.push(isDarkPalette ? "dark" : "light");

View File

@ -22,7 +22,7 @@ const StoreWrapper: FC<TStoreWrapper> = observer((props) => {
const { sidebarCollapsed, toggleSidebar } = useAppTheme(); const { sidebarCollapsed, toggleSidebar } = useAppTheme();
const { data: userProfile } = useUserProfile(); const { data: userProfile } = useUserProfile();
// states // states
const [dom, setDom] = useState<undefined | HTMLElement>(); const [dom, setDom] = useState<HTMLElement | null>(null);
/** /**
* Sidebar collapsed fetching from local storage * Sidebar collapsed fetching from local storage
@ -38,19 +38,40 @@ const StoreWrapper: FC<TStoreWrapper> = observer((props) => {
* Setting up the theme of the user by fetching it from local storage * Setting up the theme of the user by fetching it from local storage
*/ */
useEffect(() => { useEffect(() => {
if (!userProfile?.theme?.theme) return;
if (window) setDom(() => window.document?.querySelector<HTMLElement>("[data-theme='custom']") || undefined);
setTheme(userProfile?.theme?.theme || "system"); setTheme(userProfile?.theme?.theme || "system");
if (userProfile?.theme?.theme === "custom" && userProfile?.theme?.palette && dom) if (!userProfile?.theme?.theme) return;
if (userProfile?.theme?.theme === "custom" && userProfile?.theme?.palette) {
applyTheme( applyTheme(
userProfile?.theme?.palette !== ",,,," userProfile?.theme?.palette !== ",,,,"
? userProfile?.theme?.palette ? userProfile?.theme?.palette
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5", : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
false false,
dom
); );
else unsetCustomCssVariables(); } else unsetCustomCssVariables();
}, [userProfile, userProfile?.theme?.theme, userProfile?.theme?.palette, setTheme, dom]); }, [userProfile, userProfile?.theme, userProfile?.theme?.palette, setTheme, dom]);
useEffect(() => {
if (dom) return;
const observer = new MutationObserver((mutationsList, observer) => {
for (const mutation of mutationsList) {
if (mutation.type === "childList") {
const customThemeElement = window.document?.querySelector<HTMLElement>("[data-theme='custom']");
if (customThemeElement) {
setDom(customThemeElement);
observer.disconnect();
break;
}
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
return () => observer.disconnect();
}, [dom]);
useEffect(() => { useEffect(() => {
if (!router.query) return; if (!router.query) return;

View File

@ -69,6 +69,10 @@ const nextConfig = {
const ADMIN_BASE_URL = process.env.NEXT_PUBLIC_ADMIN_BASE_URL || ""; const ADMIN_BASE_URL = process.env.NEXT_PUBLIC_ADMIN_BASE_URL || "";
const ADMIN_BASE_PATH = process.env.NEXT_PUBLIC_ADMIN_BASE_PATH || ""; const ADMIN_BASE_PATH = process.env.NEXT_PUBLIC_ADMIN_BASE_PATH || "";
const GOD_MODE_BASE_URL = ADMIN_BASE_URL + ADMIN_BASE_PATH; const GOD_MODE_BASE_URL = ADMIN_BASE_URL + ADMIN_BASE_PATH;
rewrites.push({
source: "/god-mode",
destination: `${GOD_MODE_BASE_URL}/`,
})
rewrites.push({ rewrites.push({
source: "/god-mode/:path*", source: "/god-mode/:path*",
destination: `${GOD_MODE_BASE_URL}/:path*`, destination: `${GOD_MODE_BASE_URL}/:path*`,

View File

@ -293,24 +293,26 @@ export class CycleIssues extends IssueHelperStore implements ICycleIssues {
const response = await this.createIssue(workspaceSlug, projectId, data, cycleId); const response = await this.createIssue(workspaceSlug, projectId, data, cycleId);
if (data.module_ids && data.module_ids.length > 0) const quickAddIssueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === data.id);
if (quickAddIssueIndex >= 0) {
runInAction(() => {
this.issues[cycleId].splice(quickAddIssueIndex, 1);
this.rootIssueStore.issues.removeIssue(data.id);
});
}
if (data.module_ids && data.module_ids.length > 0) {
await this.rootStore.moduleIssues.changeModulesInIssue( await this.rootStore.moduleIssues.changeModulesInIssue(
workspaceSlug, workspaceSlug,
projectId, projectId,
response.id, response.id,
data.module_ids, data.module_ids,
[] []
); )
}
this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId); this.rootIssueStore.rootStore.cycle.fetchCycleDetails(workspaceSlug, projectId, cycleId);
const quickAddIssueIndex = this.issues[cycleId].findIndex((_issueId) => _issueId === data.id);
if (quickAddIssueIndex >= 0)
runInAction(() => {
this.issues[cycleId].splice(quickAddIssueIndex, 1);
this.rootIssueStore.issues.removeIssue(data.id);
});
return response; return response;
} catch (error) { } catch (error) {
if (cycleId) this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId); if (cycleId) this.fetchIssues(workspaceSlug, projectId, "mutation", cycleId);

View File

@ -111,8 +111,8 @@ export class IssueStore implements IIssueStore {
// store handlers from issue detail // store handlers from issue detail
// parent // parent
if (issue && issue?.parent && issue?.parent?.id) { if (issue && issue?.parent && issue?.parent?.id && issue?.parent?.project_id) {
const parentIssue = await this.issueService.retrieve(workspaceSlug, projectId, issue?.parent?.id); const parentIssue = await this.issueService.retrieve(workspaceSlug, issue.parent.project_id, issue?.parent?.id);
this.rootIssueDetailStore.rootIssueStore.issues.addIssue([parentIssue]); this.rootIssueDetailStore.rootIssueStore.issues.addIssue([parentIssue]);
} }
// assignees // assignees

View File

@ -294,17 +294,19 @@ export class ModuleIssues extends IssueHelperStore implements IModuleIssues {
const response = await this.createIssue(workspaceSlug, projectId, data, moduleId); const response = await this.createIssue(workspaceSlug, projectId, data, moduleId);
if (data.cycle_id && data.cycle_id !== "")
await this.rootStore.cycleIssues.addIssueToCycle(workspaceSlug, projectId, data.cycle_id, [response.id]);
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
const quickAddIssueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === data.id); const quickAddIssueIndex = this.issues[moduleId].findIndex((_issueId) => _issueId === data.id);
if (quickAddIssueIndex >= 0) if (quickAddIssueIndex >= 0) {
runInAction(() => { runInAction(() => {
this.issues[moduleId].splice(quickAddIssueIndex, 1); this.issues[moduleId].splice(quickAddIssueIndex, 1);
this.rootIssueStore.issues.removeIssue(data.id); this.rootIssueStore.issues.removeIssue(data.id);
}); });
}
if (data.cycle_id && data.cycle_id !== "") {
await this.rootStore.cycleIssues.addCycleToIssue(workspaceSlug, projectId, data.cycle_id, response.id)
}
this.rootIssueStore.rootStore.module.fetchModuleDetails(workspaceSlug, projectId, moduleId);
return response; return response;
} catch (error) { } catch (error) {

View File

@ -239,24 +239,27 @@ export class ProjectViewIssues extends IssueHelperStore implements IProjectViewI
const response = await this.createIssue(workspaceSlug, projectId, data, viewId); const response = await this.createIssue(workspaceSlug, projectId, data, viewId);
if (data.cycle_id && data.cycle_id !== "") const quickAddIssueIndex = this.issues[viewId].findIndex((_issueId) => _issueId === data.id);
await this.rootStore.cycleIssues.addIssueToCycle(workspaceSlug, projectId, data.cycle_id, [response.id]); if (quickAddIssueIndex >= 0) {
runInAction(() => {
this.issues[viewId].splice(quickAddIssueIndex, 1);
this.rootIssueStore.issues.removeIssue(data.id);
});
}
if (data.module_ids && data.module_ids.length > 0) if (data.cycle_id && data.cycle_id !== "") {
await this.rootStore.cycleIssues.addCycleToIssue(workspaceSlug, projectId, data.cycle_id, response.id)
}
if (data.module_ids && data.module_ids.length > 0) {
await this.rootStore.moduleIssues.changeModulesInIssue( await this.rootStore.moduleIssues.changeModulesInIssue(
workspaceSlug, workspaceSlug,
projectId, projectId,
response.id, response.id,
data.module_ids, data.module_ids,
[] []
); )
}
const quickAddIssueIndex = this.issues[viewId].findIndex((_issueId) => _issueId === data.id);
if (quickAddIssueIndex >= 0)
runInAction(() => {
this.issues[viewId].splice(quickAddIssueIndex, 1);
this.rootIssueStore.issues.removeIssue(data.id);
});
return response; return response;
} catch (error) { } catch (error) {

View File

@ -243,24 +243,29 @@ export class ProjectIssues extends IssueHelperStore implements IProjectIssues {
const response = await this.createIssue(workspaceSlug, projectId, data); const response = await this.createIssue(workspaceSlug, projectId, data);
if (data.cycle_id && data.cycle_id !== "") const quickAddIssueIndex = this.issues[projectId].findIndex((_issueId) => _issueId === data.id);
await this.rootStore.cycleIssues.addIssueToCycle(workspaceSlug, projectId, data.cycle_id, [response.id]);
if (data.module_ids && data.module_ids.length > 0) if (quickAddIssueIndex >= 0) {
runInAction(() => {
this.issues[projectId].splice(quickAddIssueIndex, 1);
this.rootStore.issues.removeIssue(data.id);
});
}
//TODO: error handling needs to be improved for rare cases
if (data.cycle_id && data.cycle_id !== "") {
await this.rootStore.cycleIssues.addCycleToIssue(workspaceSlug, projectId, data.cycle_id, response.id)
}
if (data.module_ids && data.module_ids.length > 0) {
await this.rootStore.moduleIssues.changeModulesInIssue( await this.rootStore.moduleIssues.changeModulesInIssue(
workspaceSlug, workspaceSlug,
projectId, projectId,
response.id, response.id,
data.module_ids, data.module_ids,
[] []
); )
}
const quickAddIssueIndex = this.issues[projectId].findIndex((_issueId) => _issueId === data.id);
if (quickAddIssueIndex >= 0)
runInAction(() => {
this.issues[projectId].splice(quickAddIssueIndex, 1);
this.rootStore.issues.removeIssue(data.id);
});
return response; return response;
} catch (error) { } catch (error) {
this.fetchIssues(workspaceSlug, projectId, "mutation"); this.fetchIssues(workspaceSlug, projectId, "mutation");