Merge branch 'preview' of github.com:makeplane/plane into auth-fixes

This commit is contained in:
pablohashescobar 2024-05-11 19:38:59 +05:30
commit 2501a8bbab
46 changed files with 257 additions and 184 deletions

View File

@ -7,9 +7,10 @@ import { Transition } from "@headlessui/react";
import { ExternalLink, FileText, HelpCircle, MoveLeft } from "lucide-react";
import { DiscordIcon, GithubIcon, Tooltip } from "@plane/ui";
// hooks
import { useInstance, useTheme } from "@/hooks/store";
import { useTheme } from "@/hooks/store";
// assets
import packageJson from "package.json";
import { WEB_BASE_URL } from "@/helpers/common.helper";
const helpOptions = [
{
@ -30,8 +31,6 @@ const helpOptions = [
];
export const HelpSection: FC = observer(() => {
// hooks
const { instance } = useInstance();
// states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
// store
@ -39,7 +38,7 @@ export const HelpSection: FC = observer(() => {
// refs
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
const redirectionLink = `${instance?.config?.app_base_url ? `${instance?.config?.app_base_url}/create-workspace` : `/god-mode/`}`;
const redirectionLink = encodeURI(WEB_BASE_URL + "/create-workspace");
return (
<div

View File

@ -25,7 +25,7 @@
"mobx-react-lite": "^4.0.5",
"next": "^14.2.3",
"next-themes": "^0.2.1",
"postcss": "8.4.23",
"postcss": "^8.4.38",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.51.0",

View File

@ -38,6 +38,8 @@ class InstanceEndpoint(BaseAPIView):
@cache_response(60 * 60 * 2, user=False)
def get(self, request):
instance = Instance.objects.first()
print("Instance: ", instance)
# get the instance
if instance is None:
return Response(

View File

@ -30,19 +30,6 @@ INTERNAL_IPS = ("127.0.0.1",)
MEDIA_URL = "/uploads/"
MEDIA_ROOT = os.path.join(BASE_DIR, "uploads") # noqa
CORS_ALLOWED_ORIGINS = [
"http://localhost",
"http://127.0.0.1",
"http://localhost:3000",
"http://127.0.0.1:3000",
"http://localhost:3001",
"http://127.0.0.1:3001",
"http://localhost:3002",
"http://127.0.0.1:3002",
]
CSRF_TRUSTED_ORIGINS = CORS_ALLOWED_ORIGINS
CORS_ALLOW_ALL_ORIGINS = True
LOG_DIR = os.path.join(BASE_DIR, "logs") # noqa
if not os.path.exists(LOG_DIR):

View File

@ -21,6 +21,7 @@ from rest_framework.viewsets import ModelViewSet
# Module imports
from plane.utils.exception_logger import log_exception
from plane.utils.paginator import BasePaginator
from plane.authentication.session import BaseSessionAuthentication
class TimezoneMixin:
@ -49,6 +50,10 @@ class BaseViewSet(TimezoneMixin, ModelViewSet, BasePaginator):
SearchFilter,
)
authentication_classes = [
BaseSessionAuthentication,
]
filterset_fields = []
search_fields = []
@ -146,6 +151,10 @@ class BaseAPIView(TimezoneMixin, APIView, BasePaginator):
search_fields = []
authentication_classes = [
BaseSessionAuthentication,
]
def filter_queryset(self, queryset):
for backend in list(self.filter_backends):
queryset = backend().filter_queryset(self.request, queryset, self)

View File

@ -25,7 +25,7 @@
"devDependencies": {
"autoprefixer": "^10.4.15",
"eslint-config-custom": "*",
"postcss": "^8.4.29",
"postcss": "^8.4.38",
"prettier": "latest",
"prettier-plugin-tailwindcss": "^0.5.4",
"tailwindcss": "^3.3.3",

View File

@ -61,7 +61,7 @@
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"eslint-config-custom": "*",
"postcss": "^8.4.29",
"postcss": "^8.4.38",
"tailwind-config-custom": "*",
"tsconfig": "*",
"tsup": "^7.2.0",

View File

@ -46,7 +46,7 @@
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"eslint-config-custom": "*",
"postcss": "^8.4.29",
"postcss": "^8.4.38",
"tailwind-config-custom": "*",
"tsconfig": "*",
"tsup": "^7.2.0",

View File

@ -42,7 +42,7 @@
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"eslint-config-custom": "*",
"postcss": "^8.4.29",
"postcss": "^8.4.38",
"tailwind-config-custom": "*",
"tsconfig": "*",
"tsup": "^7.2.0",

View File

@ -37,7 +37,7 @@
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"eslint-config-custom": "*",
"postcss": "^8.4.29",
"postcss": "^8.4.38",
"tailwind-config-custom": "*",
"tsconfig": "*",
"tsup": "^7.2.0",

View File

@ -39,7 +39,7 @@
"@types/react": "^18.2.42",
"@types/react-dom": "^18.2.17",
"eslint-config-custom": "*",
"postcss": "^8.4.29",
"postcss": "^8.4.38",
"react": "^18.2.0",
"tailwind-config-custom": "*",
"tsconfig": "*",

View File

@ -7,7 +7,7 @@
"devDependencies": {
"@tailwindcss/typography": "^0.5.9",
"autoprefixer": "^10.4.14",
"postcss": "^8.4.21",
"postcss": "^8.4.38",
"prettier": "^2.8.8",
"prettier-plugin-tailwindcss": "^0.3.0",
"tailwindcss": "^3.2.7",

View File

@ -1 +1,2 @@
export * from "./not-ready-view";
export * from "./not-ready-view";
export * from "./instance-failure-view";

View File

@ -0,0 +1,38 @@
import { FC } from "react";
import Image from "next/image";
import { useTheme } from "next-themes";
import { Button } from "@plane/ui";
// assets
import InstanceFailureDarkImage from "public/instance/instance-failure-dark.svg";
import InstanceFailureImage from "public/instance/instance-failure.svg";
type InstanceFailureViewProps = {
mutate: () => void;
};
export const InstanceFailureView: FC<InstanceFailureViewProps> = (props) => {
const { mutate } = props;
const { resolvedTheme } = useTheme();
const instanceImage = resolvedTheme === "dark" ? InstanceFailureDarkImage : InstanceFailureImage;
return (
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center mt-10">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<Image src={instanceImage} alt="Plane Logo" />
<h3 className="font-medium text-2xl text-white ">Unable to fetch instance details.</h3>
<p className="font-medium text-base text-center">
We were unable to fetch the details of the instance. <br />
Fret not, it might just be a connectivity issue.
</p>
</div>
<div className="flex justify-center">
<Button size="md" onClick={mutate}>
Retry
</Button>
</div>
</div>
</div>
);
};

View File

@ -1,40 +1,33 @@
import { FC } from "react";
import Image from "next/image";
import { useTheme } from "next-themes";
// icons
import { UserCog2 } from "lucide-react";
import Link from "next/link";
// ui
import { getButtonStyling } from "@plane/ui";
import { Button } from "@plane/ui";
// helpers
import { ADMIN_BASE_URL, ADMIN_BASE_PATH } from "@/helpers/common.helper";
// images
import instanceNotReady from "public/instance/plane-instance-not-ready.webp";
import PlaneBlackLogo from "public/plane-logos/black-horizontal-with-blue-logo.svg";
import PlaneWhiteLogo from "public/plane-logos/white-horizontal-with-blue-logo.svg";
import PlaneTakeOffImage from "@/public/instance/plane-takeoff.png";
export const InstanceNotReady: FC = () => {
const { resolvedTheme } = useTheme();
const planeLogo = resolvedTheme === "dark" ? PlaneWhiteLogo : PlaneBlackLogo;
const GOD_MODE_URL = encodeURI(ADMIN_BASE_URL + ADMIN_BASE_PATH + "/setup/?auth_enabled=0");
return (
<div className="h-screen w-full overflow-y-auto bg-onboarding-gradient-100">
<div className="h-full w-full pt-24">
<div className="mx-auto h-full rounded-t-md border-x border-t border-custom-border-100 bg-onboarding-gradient-100 px-4 pt-4 shadow-sm sm:w-4/5 md:w-2/3">
<div className="relative h-full rounded-t-md bg-onboarding-gradient-200 px-7 sm:px-0">
<div className="flex items-center justify-center py-10">
<Image src={planeLogo} className="h-[44px] w-full" alt="Plane logo" />
</div>
<div className="mt-20">
<Image src={instanceNotReady} className="w-full" alt="Instance not ready" />
</div>
<div className="flex w-full flex-col items-center gap-5 py-12 pb-20">
<h3 className="text-2xl font-medium">Your Plane instance isn{"'"}t ready yet</h3>
<p className="text-sm">Ask your Instance Admin to complete set-up first.</p>
<a href="/god-mode" className={`${getButtonStyling("primary", "md")} mt-4`}>
<UserCog2 className="h-3.5 w-3.5" />
Get started
</a>
</div>
</div>
<div className="h-full w-full relative container px-5 mx-auto flex justify-center items-center mt-10">
<div className="w-auto max-w-2xl relative space-y-8 py-10">
<div className="relative flex flex-col justify-center items-center space-y-4">
<h1 className="text-3xl font-bold pb-3">Welcome aboard Plane!</h1>
<Image src={PlaneTakeOffImage} alt="Plane Logo" />
<p className="font-medium text-base text-onboarding-text-400">
Get started by setting up your instance and workspace
</p>
</div>
<div>
<Link href={GOD_MODE_URL}>
<Button size="lg" className="w-full">
Get started
</Button>
</Link>
</div>
</div>
</div>

View File

@ -3,6 +3,9 @@ import { twMerge } from "tailwind-merge";
export const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "";
export const ADMIN_BASE_URL = process.env.NEXT_PUBLIC_ADMIN_BASE_URL || "";
export const ADMIN_BASE_PATH = process.env.NEXT_PUBLIC_ADMIN_BASE_PATH || "";
export const SPACE_BASE_PATH = process.env.NEXT_PUBLIC_SPACE_BASE_PATH || "";
export const WEB_BASE_URL = process.env.NEXT_PUBLIC_WEB_BASE_URL || "";

View File

@ -1,10 +1,9 @@
import Image from "next/image";
// mobx
import { observer } from "mobx-react-lite";
import planeLogo from "public/plane-logo.svg";
import Image from "next/image";
// components
import IssueNavbar from "@/components/issues/navbar";
// logo
import planeLogo from "public/plane-logo.svg";
const ProjectLayout = ({ children }: { children: React.ReactNode }) => (
<div className="relative flex h-screen min-h-[500px] w-screen flex-col overflow-hidden">
@ -12,7 +11,6 @@ const ProjectLayout = ({ children }: { children: React.ReactNode }) => (
<IssueNavbar />
</div>
<div className="relative h-full w-full overflow-hidden bg-custom-background-90">{children}</div>
<a
href="https://plane.so"
className="fixed bottom-2.5 right-5 !z-[999999] flex items-center gap-1 rounded border border-custom-border-200 bg-custom-background-100 px-2 py-1 shadow-custom-shadow-2xs"

View File

@ -32,6 +32,7 @@ export const AuthWrapper: FC<TAuthWrapper> = observer((props) => {
<Spinner />
</div>
);
if (pageType === EPageTypes.PUBLIC) return <>{children}</>;
if (pageType === EPageTypes.INIT) {

View File

@ -4,7 +4,7 @@ import useSWR from "swr";
// ui
import { Spinner } from "@plane/ui";
// components
import { InstanceNotReady } from "@/components/instance";
import { InstanceNotReady, InstanceFailureView } from "@/components/instance";
// hooks
import { useInstance } from "@/hooks/store";
@ -17,8 +17,11 @@ export const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
// hooks
const { isLoading, instance, fetchInstanceInfo } = useInstance();
const { isLoading: isSWRLoading } = useSWR("INSTANCE_INFORMATION", () => fetchInstanceInfo(), {
const { isLoading: isSWRLoading, mutate } = useSWR("INSTANCE_INFORMATION", () => fetchInstanceInfo(), {
revalidateOnFocus: false,
revalidateIfStale: false,
revalidateOnReconnect: false,
errorRetryCount: 0,
});
if (isSWRLoading || isLoading)
@ -28,6 +31,8 @@ export const InstanceWrapper: FC<TInstanceWrapper> = observer((props) => {
</div>
);
if (!instance) return <InstanceFailureView mutate={mutate} />;
if (instance?.instance?.is_setup_done === false) return <InstanceNotReady />;
return <>{children}</>;

View File

@ -0,0 +1,40 @@
<svg width="210" height="206" viewBox="0 0 210 206" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="107.5" cy="103" r="102.5" fill="#24252C"/>
<path d="M140.625 162.125V148.875C138.868 148.875 137.183 148.177 135.94 146.935C134.698 145.692 134 144.007 134 142.25V135.625C134 132.111 135.396 128.741 137.881 126.256C140.366 123.771 143.736 122.375 147.25 122.375H160.5C164.014 122.375 167.384 123.771 169.869 126.256C172.354 128.741 173.75 132.111 173.75 135.625V142.25C173.75 144.007 173.052 145.692 171.81 146.935C170.567 148.177 168.882 148.875 167.125 148.875" stroke="#454961" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M153.875 122.375V66.0625C153.875 59.9128 151.432 54.015 147.084 49.6665C142.735 45.318 136.837 42.875 130.687 42.875C124.538 42.875 118.64 45.318 114.291 49.6665C109.943 54.015 107.5 59.9128 107.5 66.0625M107.5 138.937C107.5 145.087 105.057 150.985 100.709 155.334C96.36 159.682 90.4622 162.125 84.3125 162.125C78.1628 162.125 72.265 159.682 67.9165 155.334C63.568 150.985 61.125 145.087 61.125 138.937V82.625" stroke="#454961" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M167.125 162.125V148.875H140.625" stroke="#454961" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M47.875 56.125H74.375V42.875" stroke="#454961" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M74.375 56.125C76.1321 56.125 77.8172 56.823 79.0596 58.0654C80.302 59.3078 81 60.9929 81 62.75V69.375C81 72.8891 79.604 76.2593 77.1192 78.7442C74.6343 81.229 71.2641 82.625 67.75 82.625H54.5C50.9859 82.625 47.6157 81.229 45.1308 78.7442C42.646 76.2593 41.25 72.8891 41.25 69.375V62.75C41.25 60.9929 41.948 59.3078 43.1904 58.0654C44.4328 56.823 46.1179 56.125 47.875 56.125V42.875" stroke="#454961" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<g filter="url(#filter0_ddd_11437_265561)">
<circle cx="107.911" cy="102.911" r="23.7938" fill="#3A5BC7"/>
<path d="M114.051 96.7712L101.771 109.052" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M101.771 96.7712L114.051 109.052" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<filter id="filter0_ddd_11437_265561" x="76.1172" y="74.1177" width="63.5879" height="64.5876" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feMorphology radius="2" operator="erode" in="SourceAlpha" result="effect1_dropShadow_11437_265561"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0 0 0 0.051 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_11437_265561"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feMorphology radius="4" operator="erode" in="SourceAlpha" result="effect2_dropShadow_11437_265561"/>
<feOffset dy="3"/>
<feGaussianBlur stdDeviation="6"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.055 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_11437_265561" result="effect2_dropShadow_11437_265561"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feMorphology radius="8" operator="erode" in="SourceAlpha" result="effect3_dropShadow_11437_265561"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="8"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.078 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_11437_265561" result="effect3_dropShadow_11437_265561"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect3_dropShadow_11437_265561" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -0,0 +1,40 @@
<svg width="210" height="206" viewBox="0 0 210 206" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="107.5" cy="103" r="102.5" fill="#F3F6FF"/>
<path d="M140.625 162.125V148.875C138.868 148.875 137.183 148.177 135.94 146.935C134.698 145.692 134 144.007 134 142.25V135.625C134 132.111 135.396 128.741 137.881 126.256C140.366 123.771 143.736 122.375 147.25 122.375H160.5C164.014 122.375 167.384 123.771 169.869 126.256C172.354 128.741 173.75 132.111 173.75 135.625V142.25C173.75 144.007 173.052 145.692 171.81 146.935C170.567 148.177 168.882 148.875 167.125 148.875" stroke="#3E63DD" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M153.875 122.375V66.0625C153.875 59.9128 151.432 54.015 147.084 49.6665C142.735 45.318 136.837 42.875 130.687 42.875C124.538 42.875 118.64 45.318 114.291 49.6665C109.943 54.015 107.5 59.9128 107.5 66.0625M107.5 138.937C107.5 145.087 105.057 150.985 100.709 155.334C96.36 159.682 90.4622 162.125 84.3125 162.125C78.1628 162.125 72.265 159.682 67.9165 155.334C63.568 150.985 61.125 145.087 61.125 138.937V82.625" stroke="#3E63DD" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M167.125 162.125V148.875H140.625" stroke="#3E63DD" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M47.875 56.125H74.375V42.875" stroke="#3E63DD" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M74.375 56.125C76.1321 56.125 77.8172 56.823 79.0596 58.0654C80.302 59.3078 81 60.9929 81 62.75V69.375C81 72.8891 79.604 76.2593 77.1192 78.7442C74.6343 81.229 71.2641 82.625 67.75 82.625H54.5C50.9859 82.625 47.6157 81.229 45.1308 78.7442C42.646 76.2593 41.25 72.8891 41.25 69.375V62.75C41.25 60.9929 41.948 59.3078 43.1904 58.0654C44.4328 56.823 46.1179 56.125 47.875 56.125V42.875" stroke="#3E63DD" stroke-width="8" stroke-linecap="round" stroke-linejoin="round"/>
<g filter="url(#filter0_ddd_11424_265422)">
<circle cx="107.911" cy="102.911" r="23.7938" fill="#3A5BC7"/>
<path d="M114.051 96.7712L101.771 109.052" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M101.771 96.7712L114.051 109.052" stroke="white" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<filter id="filter0_ddd_11424_265422" x="76.1172" y="74.1177" width="63.5879" height="64.5876" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feMorphology radius="2" operator="erode" in="SourceAlpha" result="effect1_dropShadow_11424_265422"/>
<feOffset dy="2"/>
<feGaussianBlur stdDeviation="1.5"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.24 0 0 0 0.051 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_11424_265422"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feMorphology radius="4" operator="erode" in="SourceAlpha" result="effect2_dropShadow_11424_265422"/>
<feOffset dy="3"/>
<feGaussianBlur stdDeviation="6"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.055 0"/>
<feBlend mode="normal" in2="effect1_dropShadow_11424_265422" result="effect2_dropShadow_11424_265422"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feMorphology radius="8" operator="erode" in="SourceAlpha" result="effect3_dropShadow_11424_265422"/>
<feOffset dy="4"/>
<feGaussianBlur stdDeviation="8"/>
<feComposite in2="hardAlpha" operator="out"/>
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.078 0"/>
<feBlend mode="normal" in2="effect2_dropShadow_11424_265422" result="effect3_dropShadow_11424_265422"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect3_dropShadow_11424_265422" result="shape"/>
</filter>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -2,7 +2,7 @@ import { FC, MouseEvent, useRef } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useRouter } from "next/router";
import { Info } from "lucide-react";
import { CalendarCheck2, CalendarClock, Info, MoveRight } from "lucide-react";
// types
import type { TCycleGroups } from "@plane/types";
// ui
@ -226,12 +226,14 @@ export const CyclesBoardCard: FC<ICyclesBoardCard> = observer((props) => {
</Tooltip>
<div className="flex items-center justify-between">
{isDateValid ? (
<span className="text-xs text-custom-text-300">
{renderFormattedDate(startDate) ?? "_ _"} - {renderFormattedDate(endDate) ?? "_ _"}
</span>
) : (
<span className="text-xs text-custom-text-400">No due date</span>
{isDateValid && (
<div className="h-6 flex items-center gap-1.5 text-custom-text-300 border-[0.5px] border-custom-border-300 rounded text-xs px-2 cursor-default">
<CalendarClock className="h-3 w-3 flex-shrink-0" />
<span className="flex-grow truncate">{renderFormattedDate(startDate)}</span>
<MoveRight className="h-3 w-3 flex-shrink-0" />
<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />
<span className="flex-grow truncate">{renderFormattedDate(endDate)}</span>
</div>
)}
</div>
</div>

View File

@ -1,6 +1,6 @@
import React, { FC, MouseEvent } from "react";
import { observer } from "mobx-react";
import { User2 } from "lucide-react";
import { CalendarCheck2, CalendarClock, MoveRight, User2 } from "lucide-react";
// types
import { ICycle, TCycleGroups } from "@plane/types";
// ui
@ -106,9 +106,15 @@ export const CycleListItemAction: FC<Props> = observer((props) => {
return (
<>
<div className="text-xs text-custom-text-300 flex-shrink-0">
{renderDate && `${renderFormattedDate(startDate) ?? `_ _`} - ${renderFormattedDate(endDate) ?? `_ _`}`}
</div>
{renderDate && (
<div className="h-6 flex items-center gap-1.5 text-custom-text-300 border-[0.5px] border-custom-border-300 rounded text-xs px-2 cursor-default">
<CalendarClock className="h-3 w-3 flex-shrink-0" />
<span className="flex-grow truncate">{renderFormattedDate(startDate)}</span>
<MoveRight className="h-3 w-3 flex-shrink-0" />
<CalendarCheck2 className="h-3 w-3 flex-shrink-0" />
<span className="flex-grow truncate">{renderFormattedDate(endDate)}</span>
</div>
)}
{currentCycle && (
<div

View File

@ -94,7 +94,7 @@ export const CyclesListItem: FC<TCyclesListItem> = observer((props) => {
) : progress === 100 ? (
<Check className="h-3 w-3 stroke-[2] text-custom-primary-100" />
) : (
<span className="text-xs text-custom-text-300">{`${progress}%`}</span>
<span className="text-[9px] text-custom-text-300">{`${progress}%`}</span>
)}
</CircularProgressIndicator>
}

View File

@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import Link from "next/link";
import { useRouter } from "next/router";
// icons
import { ArrowRight, Plus, PanelRight } from "lucide-react";
import { ArrowRight, PanelRight } from "lucide-react";
// types
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "@plane/types";
// ui
@ -281,7 +281,6 @@ export const CycleIssuesHeader: React.FC = observer(() => {
toggleCreateIssueModal(true, EIssuesStoreType.CYCLE);
}}
size="sm"
prependIcon={<Plus />}
>
Add Issue
</Button>

View File

@ -1,8 +1,6 @@
import { FC } from "react";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
// icons
import { Plus } from "lucide-react";
// ui
import { Breadcrumbs, Button, ContrastIcon } from "@plane/ui";
// components
@ -61,7 +59,6 @@ export const CyclesHeader: FC = observer(() => {
<Button
variant="primary"
size="sm"
prependIcon={<Plus />}
onClick={() => {
setTrackElement("Cycles page");
toggleCreateCycleModal(true);

View File

@ -1,8 +1,6 @@
import { useCallback, useState } from "react";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
// icons
import { PlusIcon } from "lucide-react";
// types
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions } from "@plane/types";
// ui
@ -134,8 +132,8 @@ export const GlobalIssuesHeader: React.FC = observer(() => {
</FiltersDropdown>
</>
{isAuthorizedUser && (
<Button variant="primary" size="sm" prependIcon={<PlusIcon />} onClick={() => setCreateViewModal(true)}>
New View
<Button variant="primary" size="sm" onClick={() => setCreateViewModal(true)}>
Add View
</Button>
)}
</div>

View File

@ -3,7 +3,7 @@ import { observer } from "mobx-react";
import Link from "next/link";
import { useRouter } from "next/router";
// icons
import { ArrowRight, PanelRight, Plus } from "lucide-react";
import { ArrowRight, PanelRight } from "lucide-react";
// types
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "@plane/types";
// ui
@ -288,7 +288,6 @@ export const ModuleIssuesHeader: React.FC = observer(() => {
toggleCreateIssueModal(true, EIssuesStoreType.MODULE);
}}
size="sm"
prependIcon={<Plus />}
>
Add Issue
</Button>

View File

@ -1,7 +1,5 @@
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
// icons
import { Plus } from "lucide-react";
// ui
import { Breadcrumbs, Button, DiceIcon } from "@plane/ui";
// components
@ -63,7 +61,6 @@ export const ModulesListHeader: React.FC = observer(() => {
<Button
variant="primary"
size="sm"
prependIcon={<Plus />}
onClick={() => {
setTrackElement("Modules page");
toggleCreateModuleModal(true);

View File

@ -1,7 +1,7 @@
import { FC } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import { FileText, Plus } from "lucide-react";
import { FileText } from "lucide-react";
// hooks
// ui
import { Breadcrumbs, Button } from "@plane/ui";
@ -79,8 +79,8 @@ export const PageDetailsHeader: FC<IPagesHeaderProps> = observer((props) => {
</div>
{showButton && (
<div className="flex items-center gap-2">
<Button variant="primary" prependIcon={<Plus />} size="sm" onClick={() => toggleCreatePageModal(true)}>
Create Page
<Button variant="primary" size="sm" onClick={() => toggleCreatePageModal(true)}>
Add Page
</Button>
</div>
)}

View File

@ -1,6 +1,6 @@
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import { FileText, Plus } from "lucide-react";
import { FileText } from "lucide-react";
// hooks
// ui
import { Breadcrumbs, Button } from "@plane/ui";
@ -59,14 +59,13 @@ export const PagesHeader = observer(() => {
<div className="flex items-center gap-2">
<Button
variant="primary"
prependIcon={<Plus />}
size="sm"
onClick={() => {
setTrackElement("Project pages page");
toggleCreatePageModal(true);
}}
>
Create Page
Add Page
</Button>
</div>
)}

View File

@ -1,7 +1,7 @@
import { FC, useState } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import { Plus, RefreshCcw } from "lucide-react";
import { RefreshCcw } from "lucide-react";
// ui
import { Breadcrumbs, Button, LayersIcon } from "@plane/ui";
// components
@ -70,7 +70,7 @@ export const ProjectInboxHeader: FC = observer(() => {
issue={undefined}
/>
<Button variant="primary" prependIcon={<Plus />} size="sm" onClick={() => setCreateIssueModal(true)}>
<Button variant="primary" size="sm" onClick={() => setCreateIssueModal(true)}>
Add Issue
</Button>
</div>

View File

@ -2,7 +2,7 @@ import { useCallback, useState } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/router";
// icons
import { Briefcase, Circle, ExternalLink, Plus } from "lucide-react";
import { Briefcase, Circle, ExternalLink } from "lucide-react";
// types
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "@plane/types";
// ui
@ -229,7 +229,6 @@ export const ProjectIssuesHeader: React.FC = observer(() => {
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
}}
size="sm"
prependIcon={<Plus />}
>
<div className="hidden sm:block">Add</div> Issue
</Button>

View File

@ -2,8 +2,6 @@ import { useCallback } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useRouter } from "next/router";
// icons
import { Plus } from "lucide-react";
// types
import { IIssueDisplayFilterOptions, IIssueDisplayProperties, IIssueFilterOptions, TIssueLayouts } from "@plane/types";
// ui
@ -241,7 +239,6 @@ export const ProjectViewIssuesHeader: React.FC = observer(() => {
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT_VIEW);
}}
size="sm"
prependIcon={<Plus />}
>
Add Issue
</Button>

View File

@ -1,6 +1,5 @@
import { observer } from "mobx-react";
import { useRouter } from "next/router";
import { Plus } from "lucide-react";
// hooks
// components
import { Breadcrumbs, PhotoFilterIcon, Button } from "@plane/ui";
@ -61,13 +60,8 @@ export const ProjectViewsHeader: React.FC = observer(() => {
<div className="flex flex-shrink-0 items-center gap-2">
<ViewListHeader />
<div>
<Button
variant="primary"
size="sm"
prependIcon={<Plus className="h-3.5 w-3.5 stroke-2" />}
onClick={() => toggleCreateViewModal(true)}
>
Create View
<Button variant="primary" size="sm" onClick={() => toggleCreateViewModal(true)}>
Add View
</Button>
</div>
</div>

View File

@ -1,6 +1,6 @@
import { useCallback, useRef, useState } from "react";
import { observer } from "mobx-react";
import { Search, Plus, Briefcase, X, ListFilter } from "lucide-react";
import { Search, Briefcase, X, ListFilter } from "lucide-react";
// types
import { TProjectFilters } from "@plane/types";
// ui
@ -167,7 +167,6 @@ export const ProjectsHeader = observer(() => {
</div>
{isAuthorizedUser && (
<Button
prependIcon={<Plus />}
size="sm"
onClick={() => {
setTrackElement("Projects page");

View File

@ -80,7 +80,7 @@ export const IssueBlockRoot: FC<Props> = observer((props) => {
canEditProperties={canEditProperties}
displayProperties={displayProperties}
nestingLevel={nestingLevel + 1}
spacingLeft={spacingLeft + (displayProperties?.key ? 19 : 0)}
spacingLeft={spacingLeft + (displayProperties?.key ? 12 : 0)}
containerRef={containerRef}
/>
))}

View File

@ -57,14 +57,14 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
setPeekIssue({ workspaceSlug, projectId: issue.project_id, issueId: issue.id });
const issue = issuesMap[issueId];
const subIssues = subIssuesStore.subIssuesByIssueId(issueId);
// const subIssues = subIssuesStore.subIssuesByIssueId(issueId);
const { isMobile } = usePlatformOS();
if (!issue) return null;
const canEditIssueProperties = canEditProperties(issue.project_id);
const projectIdentifier = getProjectIdentifierById(issue.project_id);
// if sub issues have been fetched for the issue, use that for count or use issue's sub_issues_count
const subIssuesCount = subIssues ? subIssues.length : issue.sub_issues_count;
// const subIssuesCount = subIssues ? subIssues.length : issue.sub_issues_count;
const paddingLeft = `${spacingLeft}px`;
@ -86,25 +86,28 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
<div
ref={parentRef}
className={cn(
"min-h-[52px] relative flex flex-col md:flex-row md:items-center gap-3 bg-custom-background-100 p-3 pl-8 text-sm",
"min-h-11 relative flex flex-col md:flex-row md:items-center gap-3 bg-custom-background-100 p-3 pl-1.5 text-sm",
{
"border border-custom-primary-70 hover:border-custom-primary-70": getIsIssuePeeked(issue.id),
"last:border-b-transparent": !getIsIssuePeeked(issue.id),
}
)}
>
<div className="flex w-full truncate" style={nestingLevel !== 0 ? { paddingLeft } : {}}>
<div className="flex w-full truncate" style={issue?.parent_id && nestingLevel !== 0 ? { paddingLeft } : {}}>
<div className="flex flex-grow items-center gap-3 truncate">
<div className="flex items-center gap-1.5">
<div className="flex h-5 w-5 items-center justify-center">
{subIssuesCount > 0 && (
<button
className="flex items-center justify-center h-5 w-5 cursor-pointer rounded-sm text-custom-text-400 hover:text-custom-text-300"
onClick={handleToggleExpand}
>
<ChevronRight className={`h-4 w-4 ${isExpanded ? "rotate-90" : ""}`} />
</button>
)}
<div className="flex items-center gap-0.5">
<div className="flex items-center group">
<span className="size-3.5" />
<div className="flex h-4 w-4 items-center justify-center">
{issue.sub_issues_count > 0 && (
<button
className="flex items-center justify-center h-4 w-4 cursor-pointer rounded-sm text-custom-text-400 hover:text-custom-text-300"
onClick={handleToggleExpand}
>
<ChevronRight className={`h-4 w-4 ${isExpanded ? "rotate-90" : ""}`} />
</button>
)}
</div>
</div>
{displayProperties && displayProperties?.key && (
<div className="flex-shrink-0 text-xs font-medium text-custom-text-300">
@ -118,7 +121,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
</div>
{issue?.is_draft ? (
<Tooltip tooltipContent={issue.name} isMobile={isMobile}>
<Tooltip tooltipContent={issue.name} isMobile={isMobile} position="top-left">
<p className="truncate">{issue.name}</p>
</Tooltip>
) : (
@ -132,7 +135,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
className="w-full truncate cursor-pointer text-sm text-custom-text-100"
disabled={!!issue?.tempId}
>
<Tooltip tooltipContent={issue.name} isMobile={isMobile}>
<Tooltip tooltipContent={issue.name} isMobile={isMobile} position="top-left">
<p className="truncate">{issue.name}</p>
</Tooltip>
</ControlLink>
@ -151,7 +154,7 @@ export const IssueBlock: React.FC<IssueBlockProps> = observer((props: IssueBlock
{!issue?.tempId ? (
<>
<IssueProperties
className="relative flex flex-wrap items-center gap-2 whitespace-nowrap md:flex-shrink-0 md:flex-grow"
className="relative flex flex-wrap md:flex-grow md:flex-shrink-0 items-center gap-2 whitespace-nowrap"
issue={issue}
isReadOnly={!canEditIssueProperties}
updateIssue={updateIssue}

View File

@ -133,7 +133,7 @@ const GroupByList: React.FC<IGroupByList> = (props) => {
(_list: IGroupByColumn) =>
validateEmptyIssueGroups(is_list ? issueIds : issueIds?.[_list.id]) && (
<div key={_list.id} className={`flex flex-shrink-0 flex-col`}>
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-3 py-1">
<div className="sticky top-0 z-[2] w-full flex-shrink-0 border-b border-custom-border-200 bg-custom-background-90 px-3 pl-5 py-1">
<HeaderGroupByCard
icon={_list.icon}
title={_list.name || ""}

View File

@ -1,5 +1,4 @@
import { FC, useState } from "react";
import { Plus } from "lucide-react";
import { Button } from "@plane/ui";
// components
import { CreateUpdateProjectViewModal } from "@/components/views";
@ -25,7 +24,7 @@ export const SaveFilterView: FC<ISaveFilterView> = (props) => {
onClose={() => setViewModal(false)}
/>
<Button size="sm" prependIcon={<Plus />} onClick={() => setViewModal(true)}>
<Button size="sm" onClick={() => setViewModal(true)}>
Save View
</Button>
</div>

View File

@ -1,4 +1,4 @@
import { FC } from "react";
import { FC, Fragment } from "react";
import { observer } from "mobx-react-lite";
import { TIssue } from "@plane/types";
// hooks
@ -45,7 +45,7 @@ export const IssueList: FC<IIssueList> = observer((props) => {
{subIssueIds &&
subIssueIds.length > 0 &&
subIssueIds.map((issueId) => (
<>
<Fragment key={issueId}>
<IssueListItem
workspaceSlug={workspaceSlug}
projectId={projectId}
@ -56,7 +56,7 @@ export const IssueList: FC<IIssueList> = observer((props) => {
handleIssueCrudState={handleIssueCrudState}
subIssueOperations={subIssueOperations}
/>
</>
</Fragment>
))}
<div

View File

@ -1,6 +1,6 @@
import React, { FC } from "react";
import { observer } from "mobx-react";
import { Circle, Earth, Info, Lock, Minus } from "lucide-react";
import { Earth, Info, Lock, Minus } from "lucide-react";
// ui
import { Avatar, TOAST_TYPE, Tooltip, setToast } from "@plane/ui";
// components
@ -50,24 +50,16 @@ export const BlockItemAction: FC<Props> = observer((props) => {
return (
<>
{/* page details */}
<div className="flex items-center gap-2 text-custom-text-400">
{/* <span className="text-xs">Labels</span>
<Circle className="h-1 w-1 fill-custom-text-300" /> */}
<div className="cursor-default">
<Tooltip tooltipHeading="Owned by" tooltipContent={ownerDetails?.display_name}>
<Avatar src={ownerDetails?.avatar} name={ownerDetails?.display_name} />
</Tooltip>
</div>
<Circle className="h-1 w-1 fill-custom-text-300" />
{/* <span className="text-xs cursor-default">10m read</span>
<Circle className="h-1 w-1 fill-custom-text-300" /> */}
<div className="cursor-default">
<Tooltip tooltipContent={access === 0 ? "Public" : "Private"}>
{access === 0 ? <Earth className="h-3 w-3" /> : <Lock className="h-3 w-3" />}
</Tooltip>
</div>
<div className="cursor-default">
<Tooltip tooltipHeading="Owned by" tooltipContent={ownerDetails?.display_name}>
<Avatar src={ownerDetails?.avatar} name={ownerDetails?.display_name} />
</Tooltip>
</div>
<div className="cursor-default text-custom-text-300">
<Tooltip tooltipContent={access === 0 ? "Public" : "Private"}>
{access === 0 ? <Earth className="h-4 w-4" /> : <Lock className="h-4 w-4" />}
</Tooltip>
</div>
{/* vertical divider */}
<Minus className="h-5 w-5 text-custom-text-400 rotate-90 -mx-3" strokeWidth={1} />

View File

@ -1,3 +1,4 @@
import { Fragment } from "react";
import { getRandomInt, getRandomLength } from "../utils";
const ListItemRow = () => (
@ -8,13 +9,13 @@ const ListItemRow = () => (
</div>
<div className="flex items-center gap-2">
{[...Array(6)].map((_, index) => (
<>
<Fragment key={index}>
{getRandomInt(1, 2) % 2 === 0 ? (
<span key={index} className="h-5 w-5 bg-custom-background-80 rounded" />
) : (
<span className="h-5 w-16 bg-custom-background-80 rounded" />
)}
</>
</Fragment>
))}
</div>
</div>

View File

@ -1,7 +1,7 @@
import { action, makeObservable, observable } from "mobx";
// types
import { RootStore } from "@/store/root.store";
import { IUserLite } from "@plane/types";
import { RootStore } from "@/store/root.store";
import { IProjectMemberStore, ProjectMemberStore } from "./project-member.store";
import { IWorkspaceMemberStore, WorkspaceMemberStore } from "./workspace-member.store";

View File

@ -6908,15 +6908,6 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@8.4.23:
version "8.4.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.23.tgz#df0aee9ac7c5e53e1075c24a3613496f9e6552ab"
integrity sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==
dependencies:
nanoid "^3.3.6"
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@8.4.31:
version "8.4.31"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
@ -6926,7 +6917,7 @@ postcss@8.4.31:
picocolors "^1.0.0"
source-map-js "^1.0.2"
postcss@^8.4.21, postcss@^8.4.23, postcss@^8.4.29:
postcss@^8.4.23, postcss@^8.4.38:
version "8.4.38"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.38.tgz#b387d533baf2054288e337066d81c6bee9db9e0e"
integrity sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==
@ -7954,16 +7945,8 @@ streamx@^2.15.0, streamx@^2.16.1:
optionalDependencies:
bare-events "^2.2.0"
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0:
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
name string-width-cjs
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -8043,14 +8026,7 @@ stringify-object@^3.3.0:
is-obj "^1.0.1"
is-regexp "^1.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==