feat: init
This commit is contained in:
3
apps/web/core/components/global/index.ts
Normal file
3
apps/web/core/components/global/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./product-updates";
|
||||
|
||||
export * from "./timezone-select";
|
||||
66
apps/web/core/components/global/product-updates/footer.tsx
Normal file
66
apps/web/core/components/global/product-updates/footer.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { USER_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// ui
|
||||
import { getButtonStyling } from "@plane/propel/button";
|
||||
import { PlaneLogo } from "@plane/propel/icons";
|
||||
// helpers
|
||||
import { cn } from "@plane/utils";
|
||||
|
||||
export const ProductUpdatesFooter = () => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="flex items-center justify-between flex-shrink-0 gap-4 m-6 mb-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<a
|
||||
href="https://go.plane.so/p-docs"
|
||||
target="_blank"
|
||||
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
|
||||
>
|
||||
{t("docs")}
|
||||
</a>
|
||||
<svg viewBox="0 0 2 2" className="h-0.5 w-0.5 fill-current">
|
||||
<circle cx={1} cy={1} r={1} />
|
||||
</svg>
|
||||
<a
|
||||
data-ph-element={USER_TRACKER_ELEMENTS.CHANGELOG_REDIRECTED}
|
||||
href="https://go.plane.so/p-changelog"
|
||||
target="_blank"
|
||||
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
|
||||
>
|
||||
{t("full_changelog")}
|
||||
</a>
|
||||
<svg viewBox="0 0 2 2" className="h-0.5 w-0.5 fill-current">
|
||||
<circle cx={1} cy={1} r={1} />
|
||||
</svg>
|
||||
<a
|
||||
href="mailto:support@plane.so"
|
||||
target="_blank"
|
||||
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
|
||||
>
|
||||
{t("support")}
|
||||
</a>
|
||||
<svg viewBox="0 0 2 2" className="h-0.5 w-0.5 fill-current">
|
||||
<circle cx={1} cy={1} r={1} />
|
||||
</svg>
|
||||
<a
|
||||
href="https://go.plane.so/p-discord"
|
||||
target="_blank"
|
||||
className="text-sm text-custom-text-200 hover:text-custom-text-100 hover:underline underline-offset-1 outline-none"
|
||||
>
|
||||
Discord
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
href="https://plane.so/pages"
|
||||
target="_blank"
|
||||
className={cn(
|
||||
getButtonStyling("accent-primary", "sm"),
|
||||
"flex gap-1.5 items-center text-center font-medium hover:underline underline-offset-2 outline-none"
|
||||
)}
|
||||
>
|
||||
<PlaneLogo className="h-4 w-auto text-custom-text-100" />
|
||||
{t("powered_by_plane_pages")}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
2
apps/web/core/components/global/product-updates/index.ts
Normal file
2
apps/web/core/components/global/product-updates/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./modal";
|
||||
export * from "./footer";
|
||||
60
apps/web/core/components/global/product-updates/modal.tsx
Normal file
60
apps/web/core/components/global/product-updates/modal.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import type { FC } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { USER_TRACKER_ELEMENTS } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
// ui
|
||||
import { EModalPosition, EModalWidth, ModalCore } from "@plane/ui";
|
||||
// components
|
||||
import { ProductUpdatesFooter } from "@/components/global";
|
||||
// helpers
|
||||
import { captureView } from "@/helpers/event-tracker.helper";
|
||||
// hooks
|
||||
import { useInstance } from "@/hooks/store/use-instance";
|
||||
// plane web components
|
||||
import { ProductUpdatesHeader } from "@/plane-web/components/global";
|
||||
|
||||
export type ProductUpdatesModalProps = {
|
||||
isOpen: boolean;
|
||||
handleClose: () => void;
|
||||
};
|
||||
|
||||
export const ProductUpdatesModal: FC<ProductUpdatesModalProps> = observer((props) => {
|
||||
const { isOpen, handleClose } = props;
|
||||
const { t } = useTranslation();
|
||||
const { config } = useInstance();
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) {
|
||||
captureView({ elementName: USER_TRACKER_ELEMENTS.PRODUCT_CHANGELOG_MODAL });
|
||||
}
|
||||
}, [isOpen]);
|
||||
|
||||
return (
|
||||
<ModalCore isOpen={isOpen} handleClose={handleClose} position={EModalPosition.CENTER} width={EModalWidth.XXXXL}>
|
||||
<ProductUpdatesHeader />
|
||||
<div className="flex flex-col h-[60vh] vertical-scrollbar scrollbar-xs overflow-hidden overflow-y-scroll px-6 mx-0.5">
|
||||
{config?.instance_changelog_url && config?.instance_changelog_url !== "" ? (
|
||||
<iframe src={config?.instance_changelog_url} className="w-full h-full" />
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center w-full h-full mb-8">
|
||||
<div className="text-lg font-medium">{t("we_are_having_trouble_fetching_the_updates")}</div>
|
||||
<div className="text-sm text-custom-text-200">
|
||||
{t("please_visit")}
|
||||
<a
|
||||
data-ph-element={USER_TRACKER_ELEMENTS.CHANGELOG_REDIRECTED}
|
||||
href="https://go.plane.so/p-changelog"
|
||||
target="_blank"
|
||||
className="text-sm text-custom-primary-100 font-medium hover:text-custom-primary-200 underline underline-offset-1 outline-none"
|
||||
>
|
||||
{t("our_changelogs")}
|
||||
</a>{" "}
|
||||
{t("for_the_latest_updates")}.
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ProductUpdatesFooter />
|
||||
</ModalCore>
|
||||
);
|
||||
});
|
||||
53
apps/web/core/components/global/timezone-select.tsx
Normal file
53
apps/web/core/components/global/timezone-select.tsx
Normal file
@@ -0,0 +1,53 @@
|
||||
"use client";
|
||||
|
||||
import type { FC } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import { CustomSearchSelect } from "@plane/ui";
|
||||
import { cn } from "@plane/utils";
|
||||
// hooks
|
||||
import useTimezone from "@/hooks/use-timezone";
|
||||
|
||||
type TTimezoneSelect = {
|
||||
value: string | undefined;
|
||||
onChange: (value: string) => void;
|
||||
error?: boolean;
|
||||
label?: string;
|
||||
buttonClassName?: string;
|
||||
className?: string;
|
||||
optionsClassName?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
export const TimezoneSelect: FC<TTimezoneSelect> = observer((props) => {
|
||||
// props
|
||||
const {
|
||||
value,
|
||||
onChange,
|
||||
error = false,
|
||||
label = "Select a timezone",
|
||||
buttonClassName = "",
|
||||
className = "",
|
||||
optionsClassName = "",
|
||||
disabled = false,
|
||||
} = props;
|
||||
// hooks
|
||||
const { disabled: isDisabled, timezones, selectedValue } = useTimezone();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<CustomSearchSelect
|
||||
value={value}
|
||||
label={value && selectedValue ? selectedValue(value) : label}
|
||||
options={isDisabled || disabled ? [] : timezones}
|
||||
onChange={onChange}
|
||||
buttonClassName={cn(buttonClassName, {
|
||||
"border-red-500": error,
|
||||
})}
|
||||
className={cn("rounded-md border-[0.5px] !border-custom-border-200", className)}
|
||||
optionsClassName={cn("w-72", optionsClassName)}
|
||||
input
|
||||
disabled={isDisabled || disabled}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user