forked from cerc-io/snowballtools-base
Integrate SP auctions for app deployment (#2)
Part of [Service provider auctions for web deployments](https://www.notion.so/Service-provider-auctions-for-web-deployments-104a6b22d47280dbad51d28aa3a91d75) - Add support for configuring web-app deployers by - - Configuring deployer LRN (for targeted deployments) - Configuring SP auction params for deployment auction (max price and number of providers) Co-authored-by: IshaVenikar <ishavenikar7@gmail.com> Reviewed-on: cerc-io/snowballtools-base#2
This commit is contained in:
@@ -16,8 +16,11 @@
|
||||
"@bugsnag/browser-performance": "^2.4.1",
|
||||
"@bugsnag/js": "^7.22.7",
|
||||
"@bugsnag/plugin-react": "^7.22.7",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource-variable/jetbrains-mono": "^5.0.19",
|
||||
"@fontsource/inter": "^5.0.16",
|
||||
"@mui/material": "^6.1.3",
|
||||
"@radix-ui/react-avatar": "^1.0.4",
|
||||
"@radix-ui/react-checkbox": "^1.0.4",
|
||||
"@radix-ui/react-dialog": "^1.0.5",
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useForm, Controller, SubmitHandler } from 'react-hook-form';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
import { useMediaQuery } from 'usehooks-ts';
|
||||
import { AuctionParams } from 'gql-client';
|
||||
|
||||
import {
|
||||
ArrowRightCircleFilledIcon,
|
||||
LoadingIcon,
|
||||
} from 'components/shared/CustomIcon';
|
||||
import { Heading } from '../../shared/Heading';
|
||||
import { Button } from '../../shared/Button';
|
||||
import { Select, SelectOption } from 'components/shared/Select';
|
||||
import { Input } from 'components/shared/Input';
|
||||
import { useToast } from 'components/shared/Toast';
|
||||
import { useGQLClient } from '../../../context/GQLClientContext';
|
||||
|
||||
type ConfigureFormValues = {
|
||||
option: string;
|
||||
lrn?: string;
|
||||
numProviders?: number;
|
||||
maxPrice?: string;
|
||||
};
|
||||
|
||||
const Configure = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const templateId = searchParams.get('templateId');
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
|
||||
const owner = queryParams.get('owner');
|
||||
const name = queryParams.get('name');
|
||||
const defaultBranch = queryParams.get('defaultBranch');
|
||||
const fullName = queryParams.get('fullName');
|
||||
const orgSlug = queryParams.get('orgSlug');
|
||||
const templateOwner = queryParams.get('templateOwner');
|
||||
const templateRepo = queryParams.get('templateRepo');
|
||||
const isPrivate = queryParams.get('isPrivate') === 'true';
|
||||
|
||||
const navigate = useNavigate();
|
||||
const { toast, dismiss } = useToast();
|
||||
const client = useGQLClient();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { handleSubmit, control, watch } = useForm<ConfigureFormValues>({
|
||||
defaultValues: { option: 'LRN' },
|
||||
});
|
||||
|
||||
const selectedOption = watch('option');
|
||||
|
||||
const isTabletView = useMediaQuery('(min-width: 720px)'); // md:
|
||||
const buttonSize = isTabletView ? { size: 'lg' as const } : {};
|
||||
|
||||
const onSubmit: SubmitHandler<ConfigureFormValues> = useCallback(
|
||||
async (data) => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
let lrn: string | undefined;
|
||||
let auctionParams: AuctionParams | undefined;
|
||||
|
||||
if (data.option === 'LRN') {
|
||||
lrn = data.lrn;
|
||||
} else if (data.option === 'Auction') {
|
||||
auctionParams = {
|
||||
numProviders: Number(data.numProviders!),
|
||||
maxPrice: (data.maxPrice!).toString(),
|
||||
};
|
||||
}
|
||||
|
||||
if (templateId) {
|
||||
// Template-based project creation
|
||||
const projectData: any = {
|
||||
templateOwner,
|
||||
templateRepo,
|
||||
owner,
|
||||
name,
|
||||
isPrivate,
|
||||
};
|
||||
|
||||
const { addProjectFromTemplate } = await client.addProjectFromTemplate(
|
||||
orgSlug!,
|
||||
projectData,
|
||||
lrn,
|
||||
auctionParams
|
||||
);
|
||||
|
||||
data.option === 'Auction'
|
||||
? navigate(
|
||||
`/${orgSlug}/projects/create/success/${addProjectFromTemplate.id}?isAuction=true`,
|
||||
)
|
||||
: navigate(
|
||||
`/${orgSlug}/projects/create/template/deploy?projectId=${addProjectFromTemplate.id}&templateId=${templateId}`
|
||||
);
|
||||
} else {
|
||||
const { addProject } = await client.addProject(
|
||||
orgSlug!,
|
||||
{
|
||||
name: fullName!,
|
||||
prodBranch: defaultBranch!,
|
||||
repository: fullName!,
|
||||
template: 'webapp',
|
||||
},
|
||||
lrn,
|
||||
auctionParams
|
||||
);
|
||||
|
||||
data.option === 'Auction'
|
||||
? navigate(
|
||||
`/${orgSlug}/projects/create/success/${addProject.id}?isAuction=true`
|
||||
)
|
||||
: navigate(
|
||||
`/${orgSlug}/projects/create/deploy?projectId=${addProject.id}`
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error creating project:', error);
|
||||
toast({
|
||||
id: 'error-creating-project',
|
||||
title: 'Error creating project',
|
||||
variant: 'error',
|
||||
onDismiss: dismiss,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[client, isPrivate, templateId, navigate, dismiss, toast]
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="space-y-7">
|
||||
<div className="flex justify-between">
|
||||
<div className="space-y-1.5">
|
||||
<Heading as="h4" className="md:text-lg font-medium">
|
||||
Configure deployment
|
||||
</Heading>
|
||||
<Heading as="h5" className="text-sm font-sans text-elements-low-em">
|
||||
The app can be deployed by setting the deployer LRN for a single
|
||||
deployment or by creating a deployer auction for multiple
|
||||
deployments
|
||||
</Heading>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="flex flex-col gap-4 lg:gap-7 w-full">
|
||||
<div className="flex flex-col justify-start gap-3">
|
||||
<Controller
|
||||
name="option"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Select
|
||||
label="Configuration Options"
|
||||
value={
|
||||
{
|
||||
value: value || 'LRN',
|
||||
label: value === 'Auction' ? 'Create Auction' : 'Deployer LRN',
|
||||
} as SelectOption
|
||||
}
|
||||
onChange={(value) => onChange((value as SelectOption).value)}
|
||||
options={[
|
||||
{ value: 'LRN', label: 'Deployer LRN' },
|
||||
{ value: 'Auction', label: 'Create Auction' },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{selectedOption === 'LRN' && (
|
||||
<div className="flex flex-col justify-start gap-3">
|
||||
<Heading as="h5" className="text-sm font-sans text-elements-low-em">
|
||||
The app will be deployed by the configured deployer
|
||||
</Heading>
|
||||
<span className="text-sm text-elements-high-em">
|
||||
Enter LRN for deployer
|
||||
</span>
|
||||
<Controller
|
||||
name="lrn"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedOption === 'Auction' && (
|
||||
<>
|
||||
<div className="flex flex-col justify-start gap-3">
|
||||
<Heading as="h5" className="text-sm font-sans text-elements-low-em">
|
||||
Set the number of deployers and maximum price for each deployment
|
||||
</Heading>
|
||||
<span className="text-sm text-elements-high-em">
|
||||
Number of Deployers
|
||||
</span>
|
||||
<Controller
|
||||
name="numProviders"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input type="number" value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col justify-start gap-3">
|
||||
<span className="text-sm text-elements-high-em">
|
||||
Maximum Price (alnt)
|
||||
</span>
|
||||
<Controller
|
||||
name="maxPrice"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<Input type="number" value={value} onChange={onChange} />
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<Button
|
||||
{...buttonSize}
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
rightIcon={
|
||||
isLoading ? (
|
||||
<LoadingIcon className="animate-spin" />
|
||||
) : (
|
||||
<ArrowRightCircleFilledIcon />
|
||||
)
|
||||
}
|
||||
>
|
||||
{isLoading ? 'Deploying repo' : 'Deploy repo'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Configure;
|
||||
+3
-30
@@ -38,36 +38,9 @@ export const ProjectRepoCard: React.FC<ProjectRepoCardProps> = ({
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const { addProject } = await client.addProject(orgSlug, {
|
||||
name: `${repository.owner?.login}-${repository.name}`,
|
||||
prodBranch: repository.default_branch as string,
|
||||
repository: repository.full_name,
|
||||
// TODO: Compute template from repo
|
||||
template: 'webapp',
|
||||
});
|
||||
if (addProject) {
|
||||
navigate(`import?projectId=${addProject.id}`);
|
||||
} else {
|
||||
toast({
|
||||
id: 'failed-to-create-project',
|
||||
title: 'Failed to create project',
|
||||
variant: 'error',
|
||||
onDismiss: dismiss,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error((error as Error).message);
|
||||
toast({
|
||||
id: 'failed-to-create-project',
|
||||
title: 'Failed to create project',
|
||||
variant: 'error',
|
||||
onDismiss: dismiss,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
navigate(
|
||||
`configure?owner=${repository.owner?.login}&name=${repository.name}&defaultBranch=${repository.default_branch}&fullName=${repository.full_name}&orgSlug=${orgSlug}`
|
||||
);
|
||||
}, [client, repository, orgSlug, setIsLoading, navigate, toast]);
|
||||
|
||||
return (
|
||||
|
||||
+7
-2
@@ -83,7 +83,7 @@ const DeploymentDetailsCard = ({
|
||||
|
||||
return (
|
||||
<div className="flex md:flex-row flex-col gap-6 py-4 px-3 pb-6 mb-2 last:mb-0 last:pb-4 border-b border-border-separator last:border-b-transparent relative">
|
||||
<div className="flex-1 flex justify-between w-full md:max-w-[25%] lg:max-w-[28%]">
|
||||
<div className="flex-1 flex justify-between w-full md:max-w-[30%] lg:max-w-[33%]">
|
||||
<div className="flex-1 w-full space-y-2 max-w-[90%] sm:max-w-full">
|
||||
{/* DEPLOYMENT URL */}
|
||||
{deployment.url && (
|
||||
@@ -96,7 +96,12 @@ const DeploymentDetailsCard = ({
|
||||
</OverflownText>
|
||||
</Heading>
|
||||
)}
|
||||
<span className="text-sm text-elements-low-em tracking-tight">
|
||||
{deployment.deployerLrn && (
|
||||
<span className="text-sm text-elements-low-em tracking-tight block mt-2">
|
||||
Deployer LRN: {deployment.deployerLrn}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-sm text-elements-low-em tracking-tight block">
|
||||
{deployment.environment === Environment.Production
|
||||
? `Production ${deployment.isCurrent ? '(Current)' : ''}`
|
||||
: 'Preview'}
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { Auction, Project } from 'gql-client';
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
} from '@mui/material';
|
||||
|
||||
import { CheckRoundFilledIcon, LoadingIcon } from 'components/shared/CustomIcon';
|
||||
import { useGQLClient } from 'context/GQLClientContext';
|
||||
import { Button, Heading, Tag } from 'components/shared';
|
||||
|
||||
const WAIT_DURATION = 5000;
|
||||
|
||||
export const AuctionCard = ({ project }: { project: Project }) => {
|
||||
const [auctionStatus, setAuctionStatus] = useState<string>('');
|
||||
const [deployerLrns, setDeployerLrns] = useState<string[]>([]);
|
||||
const [auctionDetails, setAuctionDetails] = useState<Auction | null>(null);
|
||||
const [openDialog, setOpenDialog] = useState<boolean>(false);
|
||||
const client = useGQLClient();
|
||||
|
||||
const getIconByAuctionStatus = (status: string) =>
|
||||
status === 'completed' ? <CheckRoundFilledIcon /> : <LoadingIcon className="animate-spin" />;
|
||||
|
||||
const checkAuctionStatus = useCallback(async () => {
|
||||
const result = await client.getAuctionData(project.auctionId);
|
||||
setAuctionStatus(result.status);
|
||||
setAuctionDetails(result);
|
||||
setDeployerLrns(project.deployerLrns);
|
||||
}, [client, project.auctionId, project.deployerLrns]);
|
||||
|
||||
useEffect(() => {
|
||||
if (auctionStatus !== 'completed') {
|
||||
checkAuctionStatus();
|
||||
const intervalId = setInterval(checkAuctionStatus, WAIT_DURATION);
|
||||
|
||||
return () => clearInterval(intervalId);
|
||||
}
|
||||
}, [auctionStatus, checkAuctionStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (auctionStatus === 'completed') {
|
||||
const fetchUpdatedProject = async () => {
|
||||
// Wait for 5 secs since the project is not immediately updated with deployer LRNs
|
||||
await new Promise((resolve) => setTimeout(resolve, WAIT_DURATION));
|
||||
const updatedProject = await client.getProject(project.id);
|
||||
setDeployerLrns(updatedProject.project?.deployerLrns || []);
|
||||
};
|
||||
|
||||
fetchUpdatedProject();
|
||||
}
|
||||
}, [auctionStatus, client, project.id]);
|
||||
|
||||
const renderAuctionStatus = useCallback(
|
||||
() => (
|
||||
<Tag
|
||||
leftIcon={getIconByAuctionStatus(auctionStatus)}
|
||||
size="xs"
|
||||
type={auctionStatus === 'completed' ? 'positive' : 'emphasized'}
|
||||
>
|
||||
{auctionStatus.toUpperCase()}
|
||||
</Tag>
|
||||
),
|
||||
[auctionStatus]
|
||||
);
|
||||
|
||||
const handleOpenDialog = () => setOpenDialog(true);
|
||||
const handleCloseDialog = () => setOpenDialog(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="p-3 gap-2 rounded-xl border border-gray-200 transition-colors hover:bg-base-bg-alternate flex flex-col mt-8">
|
||||
<div className="flex justify-between items-center">
|
||||
<Heading className="text-lg leading-6 font-medium">Auction details</Heading>
|
||||
<Button onClick={handleOpenDialog} variant="tertiary" size="sm">
|
||||
View details
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center mt-1">
|
||||
<span className="text-elements-high-em text-sm font-medium tracking-tight">Auction Status</span>
|
||||
<div className="ml-2">{renderAuctionStatus()}</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between items-center mt-2">
|
||||
<span className="text-elements-high-em text-sm font-medium tracking-tight">Auction Id</span>
|
||||
<span className="text-elements-mid-em text-sm text-right">
|
||||
{project.auctionId}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{deployerLrns?.length > 0 && (
|
||||
<div className="mt-3">
|
||||
<span className="text-elements-high-em text-sm font-medium tracking-tight">Deployer LRNs</span>
|
||||
{deployerLrns.map((lrn, index) => (
|
||||
<p key={index} className="text-elements-mid-em text-sm">
|
||||
{'\u2022'} {lrn}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Dialog open={openDialog} onClose={handleCloseDialog} fullWidth maxWidth="md">
|
||||
<DialogTitle>Auction Details</DialogTitle>
|
||||
<DialogContent>
|
||||
{auctionDetails && <pre>{JSON.stringify(auctionDetails, null, 2)}</pre>}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleCloseDialog}>Close</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -60,8 +60,8 @@ const DeleteProjectDialog = ({
|
||||
<Modal.Body>
|
||||
<Input
|
||||
label={
|
||||
"Deleting your project is irreversible. Enter your project's name " +
|
||||
project.name +
|
||||
"Deleting your project is irreversible. Enter your project's name " + '"' +
|
||||
project.name + '"' +
|
||||
' below to confirm you want to permanently delete it:'
|
||||
}
|
||||
id="input"
|
||||
|
||||
@@ -36,6 +36,7 @@ const deployment: Deployment = {
|
||||
url: 'https://deploy1.example.com',
|
||||
environment: Environment.Production,
|
||||
isCurrent: true,
|
||||
deployerLrn: 'lrn://example/deployers/webapp-deployer-api.test.com',
|
||||
status: DeploymentStatus.Ready,
|
||||
createdBy: {
|
||||
id: 'user1',
|
||||
|
||||
@@ -31,6 +31,11 @@ const CreateWithTemplate = () => {
|
||||
},
|
||||
{
|
||||
step: 2,
|
||||
route: `/${orgSlug}/projects/create/template/configure`,
|
||||
label: 'Configure',
|
||||
},
|
||||
{
|
||||
step: 3,
|
||||
route: `/${orgSlug}/projects/create/template/deploy`,
|
||||
label: 'Deploy',
|
||||
},
|
||||
|
||||
@@ -2,7 +2,8 @@ import NewProject from './index';
|
||||
import CreateWithTemplate from './Template';
|
||||
import { templateRoutes } from './template/routes';
|
||||
import Id from './success/Id';
|
||||
import Import from './Import';
|
||||
import Configure from 'components/projects/create/Configure';
|
||||
import Deploy from 'components/projects/create/Deploy';
|
||||
|
||||
export const createProjectRoutes = [
|
||||
{
|
||||
@@ -19,7 +20,11 @@ export const createProjectRoutes = [
|
||||
element: <Id />,
|
||||
},
|
||||
{
|
||||
path: 'import',
|
||||
element: <Import />,
|
||||
path: 'configure',
|
||||
element: <Configure />,
|
||||
},
|
||||
{
|
||||
path: 'deploy',
|
||||
element: <Deploy />,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { Link, useParams, useSearchParams } from 'react-router-dom';
|
||||
import Lottie from 'lottie-react';
|
||||
|
||||
import { Badge } from 'components/shared/Badge';
|
||||
import { Button } from 'components/shared/Button';
|
||||
import {
|
||||
ArrowLeftCircleFilledIcon,
|
||||
LinkChainIcon,
|
||||
QuestionMarkRoundFilledIcon,
|
||||
} from 'components/shared/CustomIcon';
|
||||
import { Heading } from 'components/shared/Heading';
|
||||
@@ -19,6 +18,8 @@ const Id = () => {
|
||||
const { id, orgSlug } = useParams();
|
||||
const client = useGQLClient();
|
||||
const [project, setProject] = useState<Project | null>(null);
|
||||
const [searchParams] = useSearchParams();
|
||||
const isAuction = searchParams.get('isAuction') === 'true';
|
||||
|
||||
const handleSetupDomain = async () => {
|
||||
if (id) {
|
||||
@@ -51,22 +52,8 @@ const Id = () => {
|
||||
{/* Heading */}
|
||||
<div className="flex flex-col items-center gap-1.5">
|
||||
<Heading as="h3" className="font-medium text-xl">
|
||||
Project deployed successfully.
|
||||
{isAuction? 'Auction created successfully.' : 'Project deployment created successfully.'}
|
||||
</Heading>
|
||||
<p className="flex flex-col items-center lg:flex-row font-sans gap-0.5 lg:gap-2 text-sm text-elements-high-em">
|
||||
Your project has been deployed at{' '}
|
||||
<Button
|
||||
className="no-underline text-elements-link"
|
||||
// TODO: use dynamic value
|
||||
href={project ? `https://${project.subDomain}` : ''}
|
||||
as="a"
|
||||
variant="link-emphasized"
|
||||
external
|
||||
leftIcon={<LinkChainIcon />}
|
||||
>
|
||||
{project.subDomain}
|
||||
</Button>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Card */}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import ConfigureComponent from '../../../../../components/projects/create/Configure';
|
||||
|
||||
const Configure = () => {
|
||||
return <ConfigureComponent />;
|
||||
};
|
||||
|
||||
export default Configure;
|
||||
@@ -6,7 +6,6 @@ import { useMediaQuery } from 'usehooks-ts';
|
||||
import { RequestError } from 'octokit';
|
||||
|
||||
import { useOctokit } from '../../../../../context/OctokitContext';
|
||||
import { useGQLClient } from '../../../../../context/GQLClientContext';
|
||||
import { Template } from '../../../../../types/types';
|
||||
import { Heading } from 'components/shared/Heading';
|
||||
import { Input } from 'components/shared/Input';
|
||||
@@ -31,7 +30,6 @@ type SubmitRepoValues = {
|
||||
const CreateRepo = () => {
|
||||
const { octokit, isAuth } = useOctokit();
|
||||
const { template } = useOutletContext<{ template: Template }>();
|
||||
const client = useGQLClient();
|
||||
|
||||
const { orgSlug } = useParams();
|
||||
const { toast, dismiss } = useToast();
|
||||
@@ -55,19 +53,8 @@ const CreateRepo = () => {
|
||||
|
||||
setIsLoading(true);
|
||||
|
||||
const { addProjectFromTemplate } = await client.addProjectFromTemplate(
|
||||
orgSlug!,
|
||||
{
|
||||
templateOwner: owner,
|
||||
templateRepo: repo,
|
||||
owner: data.account,
|
||||
name: data.repoName,
|
||||
isPrivate: false,
|
||||
},
|
||||
);
|
||||
|
||||
navigate(
|
||||
`deploy?projectId=${addProjectFromTemplate.id}&templateId=${template.id}`,
|
||||
`configure?templateId=${template.id}&templateOwner=${owner}&templateRepo=${repo}&owner=${data.account}&name=${data.repoName}&isPrivate=false&orgSlug=${orgSlug}`
|
||||
);
|
||||
} catch (err) {
|
||||
setIsLoading(false);
|
||||
@@ -203,7 +190,7 @@ const CreateRepo = () => {
|
||||
)
|
||||
}
|
||||
>
|
||||
Deploy
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import CreateRepo from './index';
|
||||
import Configure from './Configure';
|
||||
import Deploy from './Deploy';
|
||||
|
||||
export const templateRoutes = [
|
||||
@@ -6,6 +7,10 @@ export const templateRoutes = [
|
||||
index: true,
|
||||
element: <CreateRepo />,
|
||||
},
|
||||
{
|
||||
path: 'configure',
|
||||
element: <Configure />,
|
||||
},
|
||||
{
|
||||
path: 'deploy',
|
||||
element: <Deploy />,
|
||||
|
||||
@@ -21,6 +21,7 @@ import { Activity } from 'components/projects/project/overview/Activity';
|
||||
import { OverviewInfo } from 'components/projects/project/overview/OverviewInfo';
|
||||
import { relativeTimeMs } from 'utils/time';
|
||||
import { Domain, DomainStatus } from 'gql-client';
|
||||
import { AuctionCard } from 'components/projects/project/overview/Activity/AuctionCard';
|
||||
|
||||
const COMMITS_PER_PAGE = 4;
|
||||
|
||||
@@ -128,12 +129,15 @@ const OverviewTabPanel = () => {
|
||||
<Heading className="text-lg leading-6 font-medium truncate">
|
||||
{project.name}
|
||||
</Heading>
|
||||
<a
|
||||
href={`https://${project.subDomain}`}
|
||||
className="text-sm text-elements-low-em tracking-tight truncate"
|
||||
>
|
||||
{project.subDomain}
|
||||
</a>
|
||||
{project.baseDomains && project.baseDomains.length > 0 && project.baseDomains.map((baseDomain, index) => (
|
||||
<a
|
||||
key={index}
|
||||
href={`https://${project.name}.${baseDomain}`}
|
||||
className="text-sm text-elements-low-em tracking-tight truncate"
|
||||
>
|
||||
{baseDomain}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<OverviewInfo label="Domain" icon={<GlobeIcon />}>
|
||||
@@ -205,6 +209,7 @@ const OverviewTabPanel = () => {
|
||||
No current deployment found.
|
||||
</p>
|
||||
)}
|
||||
{project.auctionId && <AuctionCard project={project}/>}
|
||||
</div>
|
||||
<Activity activities={activities} isLoading={fetchingActivities} />
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { Collapse } from '@snowballtools/material-tailwind-react-fork';
|
||||
import { Collapse, Checkbox } from '@snowballtools/material-tailwind-react-fork';
|
||||
|
||||
import AddEnvironmentVariableRow from 'components/projects/project/settings/AddEnvironmentVariableRow';
|
||||
import DisplayEnvironmentVariables from 'components/projects/project/settings/DisplayEnvironmentVariables';
|
||||
@@ -11,7 +11,7 @@ import { EnvironmentVariablesFormValues } from '../../../../../types';
|
||||
import HorizontalLine from 'components/HorizontalLine';
|
||||
import { Heading } from 'components/shared/Heading';
|
||||
import { Button } from 'components/shared/Button';
|
||||
import { Checkbox } from 'components/shared/Checkbox';
|
||||
// import { Checkbox } from 'components/shared/Checkbox';
|
||||
import { PlusIcon } from 'components/shared/CustomIcon';
|
||||
import { InlineNotification } from 'components/shared/InlineNotification';
|
||||
import { ProjectSettingContainer } from 'components/projects/project/settings/ProjectSettingContainer';
|
||||
|
||||
@@ -102,6 +102,7 @@ export const deployment0: Deployment = {
|
||||
domain: domain0,
|
||||
commitMessage: 'Commit Message',
|
||||
createdBy: user,
|
||||
deployerLrn: 'lrn://deployer.apps.snowballtools.com ',
|
||||
};
|
||||
|
||||
export const project: Project = {
|
||||
@@ -119,7 +120,9 @@ export const project: Project = {
|
||||
organization: organization,
|
||||
template: 'Template',
|
||||
members: [member],
|
||||
auctionId: '7553538436710373822151221341b43f577e07b0525d083cc9b2de98890138a1',
|
||||
deployerLrns: ['lrn://deployer.apps.snowballtools.com '],
|
||||
webhooks: ['beepboop'],
|
||||
icon: 'Icon',
|
||||
subDomain: 'SubDomain',
|
||||
baseDomains: ['baseDomain'],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user