forked from cerc-io/snowballtools-base
Implement organization switcher and use slug in URL (#59)
* Implement dropdown for organizations switcher * Add dynamic route for organization id * Update routes for organization slug * Use organization slug for adding project * Refactor to fetch organizations at sidebar component * Update organization switcher based on searched project * Refactor types in frontend --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
@@ -1,30 +1,66 @@
|
||||
import React from 'react';
|
||||
import { Link, NavLink } from 'react-router-dom';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Link, NavLink, useNavigate, useParams } from 'react-router-dom';
|
||||
import { Organization } from 'gql-client';
|
||||
|
||||
import { Card, CardBody, Typography } from '@material-tailwind/react';
|
||||
import { Typography, Option } from '@material-tailwind/react';
|
||||
|
||||
import { useGQLClient } from '../context/GQLClientContext';
|
||||
import AsyncSelect from './shared/AsyncSelect';
|
||||
|
||||
const Sidebar = () => {
|
||||
const { orgSlug } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const client = useGQLClient();
|
||||
|
||||
const [selectedOrgSlug, setSelectedOrgSlug] = useState(orgSlug);
|
||||
const [organizations, setOrganizations] = useState<Organization[]>([]);
|
||||
|
||||
const fetchUserOrganizations = useCallback(async () => {
|
||||
const { organizations } = await client.getOrganizations();
|
||||
setOrganizations(organizations);
|
||||
}, [orgSlug]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchUserOrganizations();
|
||||
setSelectedOrgSlug(orgSlug);
|
||||
}, [orgSlug]);
|
||||
|
||||
const Sidebar = ({ organization }: { organization: Organization }) => {
|
||||
return (
|
||||
<div className="flex flex-col h-full p-4">
|
||||
<div className="grow">
|
||||
<div>
|
||||
<Link to="/">
|
||||
<Link to={`/${orgSlug}`}>
|
||||
<h3 className="text-black text-2xl">Snowball</h3>
|
||||
</Link>
|
||||
</div>
|
||||
<Card className="-ml-1 my-2">
|
||||
<CardBody className="p-1 py-2 flex gap-2">
|
||||
<div>^</div>
|
||||
<div>
|
||||
<Typography>{organization.name}</Typography>
|
||||
<Typography>Organization</Typography>
|
||||
<AsyncSelect
|
||||
className="bg-white py-2"
|
||||
value={selectedOrgSlug}
|
||||
onChange={(value) => {
|
||||
setSelectedOrgSlug(value!);
|
||||
navigate(`/${value}`);
|
||||
}}
|
||||
selected={(_, index) => (
|
||||
<div className="flex gap-2">
|
||||
<div>^</div>
|
||||
<div>
|
||||
<span>{organizations[index!]?.name}</span>
|
||||
<Typography>Organization</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
)}
|
||||
>
|
||||
{/* TODO: Show label organization and manage in option */}
|
||||
{organizations.map((org) => (
|
||||
<Option key={org.id} value={org.slug}>
|
||||
^ {org.name}
|
||||
{org.slug === selectedOrgSlug && <p className="float-right">^</p>}
|
||||
</Option>
|
||||
))}
|
||||
</AsyncSelect>
|
||||
<div>
|
||||
<NavLink
|
||||
to="/"
|
||||
to={`/${orgSlug}`}
|
||||
className={({ isActive }) => (isActive ? 'text-blue-500' : '')}
|
||||
>
|
||||
<Typography>Projects</Typography>
|
||||
@@ -32,7 +68,7 @@ const Sidebar = ({ organization }: { organization: Organization }) => {
|
||||
</div>
|
||||
<div>
|
||||
<NavLink
|
||||
to="/settings"
|
||||
to={`/${orgSlug}/settings`}
|
||||
className={({ isActive }) => (isActive ? 'text-blue-500' : '')}
|
||||
>
|
||||
<Typography>Settings</Typography>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { Button, Typography } from '@material-tailwind/react';
|
||||
|
||||
@@ -11,9 +11,10 @@ const Deploy = () => {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const handleOpen = () => setOpen(!open);
|
||||
const navigate = useNavigate();
|
||||
const { orgSlug } = useParams();
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
navigate('/projects/create');
|
||||
navigate(`/${orgSlug}/projects/create`);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
|
||||
@@ -34,7 +34,7 @@ const TemplateCard: React.FC<TemplateCardProps> = ({
|
||||
isGitAuth,
|
||||
}) => {
|
||||
return isGitAuth ? (
|
||||
<Link to="/projects/create/template">
|
||||
<Link to="template">
|
||||
<CardDetails framework={framework} />
|
||||
</Link>
|
||||
) : (
|
||||
|
||||
+22
-23
@@ -1,4 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Environment, Project, Domain, DeploymentStatus } from 'gql-client';
|
||||
|
||||
import {
|
||||
Menu,
|
||||
@@ -9,14 +11,12 @@ import {
|
||||
Chip,
|
||||
ChipProps,
|
||||
} from '@material-tailwind/react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Environment, Project, Domain } from 'gql-client';
|
||||
|
||||
import { relativeTimeMs } from '../../../../utils/time';
|
||||
import ConfirmDialog from '../../../shared/ConfirmDialog';
|
||||
import DeploymentDialogBodyCard from './DeploymentDialogBodyCard';
|
||||
import AssignDomainDialog from './AssignDomainDialog';
|
||||
import { DeploymentDetails, Status } from '../../../../types/project';
|
||||
import { DeploymentDetails } from '../../../../types/project';
|
||||
import { useGQLClient } from '../../../../context/GQLClientContext';
|
||||
|
||||
interface DeployDetailsCardProps {
|
||||
@@ -27,10 +27,10 @@ interface DeployDetailsCardProps {
|
||||
prodBranchDomains: Domain[];
|
||||
}
|
||||
|
||||
const STATUS_COLORS: { [key in Status]: ChipProps['color'] } = {
|
||||
[Status.BUILDING]: 'blue',
|
||||
[Status.READY]: 'green',
|
||||
[Status.ERROR]: 'red',
|
||||
const STATUS_COLORS: { [key in DeploymentStatus]: ChipProps['color'] } = {
|
||||
[DeploymentStatus.Building]: 'blue',
|
||||
[DeploymentStatus.Ready]: 'green',
|
||||
[DeploymentStatus.Error]: 'red',
|
||||
};
|
||||
|
||||
const DeploymentDetailsCard = ({
|
||||
@@ -125,14 +125,12 @@ const DeploymentDetailsCard = ({
|
||||
>
|
||||
^ Assign domain
|
||||
</MenuItem>
|
||||
{!(deployment.environment === Environment.Production) && (
|
||||
<MenuItem
|
||||
onClick={() => setChangeToProduction(!changeToProduction)}
|
||||
>
|
||||
^ Change to production
|
||||
</MenuItem>
|
||||
)}
|
||||
|
||||
<MenuItem
|
||||
onClick={() => setChangeToProduction(!changeToProduction)}
|
||||
disabled={!(deployment.environment !== Environment.Production)}
|
||||
>
|
||||
^ Change to production
|
||||
</MenuItem>
|
||||
<hr className="my-3" />
|
||||
<MenuItem
|
||||
onClick={() => setRedeployToProduction(!redeployToProduction)}
|
||||
@@ -145,14 +143,15 @@ const DeploymentDetailsCard = ({
|
||||
>
|
||||
^ Redeploy to production
|
||||
</MenuItem>
|
||||
{deployment.environment === Environment.Production && (
|
||||
<MenuItem
|
||||
onClick={() => setRollbackDeployment(!rollbackDeployment)}
|
||||
disabled={deployment.isCurrent}
|
||||
>
|
||||
^ Rollback to this version
|
||||
</MenuItem>
|
||||
)}
|
||||
<MenuItem
|
||||
onClick={() => setRollbackDeployment(!rollbackDeployment)}
|
||||
disabled={
|
||||
deployment.isCurrent ||
|
||||
deployment.environment !== Environment.Production
|
||||
}
|
||||
>
|
||||
^ Rollback to this version
|
||||
</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import toast from 'react-hot-toast';
|
||||
import { Project } from 'gql-client';
|
||||
@@ -26,6 +26,7 @@ const DeleteProjectDialog = ({
|
||||
handleOpen,
|
||||
project,
|
||||
}: DeleteProjectDialogProp) => {
|
||||
const { orgSlug } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const client = useGQLClient();
|
||||
|
||||
@@ -43,7 +44,7 @@ const DeleteProjectDialog = ({
|
||||
const { deleteProject } = await client.deleteProject(project.id);
|
||||
|
||||
if (deleteProject) {
|
||||
navigate('/');
|
||||
navigate(`/${orgSlug}`);
|
||||
} else {
|
||||
toast.error('Project not deleted');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user