forked from cerc-io/snowballtools-base
		
	Create new deployment on changing preview deployment to production (#61)
* Create new deployment when changing to production * Remove unnecessary todos * Move deployment id and url creation in database method * Display correct details in deployment dialog box * Rename relativeTime function to relativeTimeISO * Refactor resolver methods to service class * Refactor to move github app to service class --------- Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
		
							parent
							
								
									e0001466e0
								
							
						
					
					
						commit
						bd6a6b330c
					
				| @ -2,6 +2,8 @@ import { DataSource, DeepPartial, FindManyOptions, FindOneOptions, FindOptionsWh | |||||||
| import path from 'path'; | import path from 'path'; | ||||||
| import debug from 'debug'; | import debug from 'debug'; | ||||||
| import assert from 'assert'; | import assert from 'assert'; | ||||||
|  | import { customAlphabet } from 'nanoid'; | ||||||
|  | import { lowercase, numbers } from 'nanoid-dictionary'; | ||||||
| 
 | 
 | ||||||
| import { DatabaseConfig } from './config'; | import { DatabaseConfig } from './config'; | ||||||
| import { User } from './entity/User'; | import { User } from './entity/User'; | ||||||
| @ -15,6 +17,8 @@ import { PROJECT_DOMAIN } from './constants'; | |||||||
| 
 | 
 | ||||||
| const log = debug('snowball:database'); | const log = debug('snowball:database'); | ||||||
| 
 | 
 | ||||||
|  | const nanoid = customAlphabet(lowercase + numbers, 8); | ||||||
|  | 
 | ||||||
| // TODO: Fix order of methods
 | // TODO: Fix order of methods
 | ||||||
| export class Database { | export class Database { | ||||||
|   private dataSource: DataSource; |   private dataSource: DataSource; | ||||||
| @ -171,7 +175,16 @@ export class Database { | |||||||
| 
 | 
 | ||||||
|   async addDeployement (data: DeepPartial<Deployment>): Promise<Deployment> { |   async addDeployement (data: DeepPartial<Deployment>): Promise<Deployment> { | ||||||
|     const deploymentRepository = this.dataSource.getRepository(Deployment); |     const deploymentRepository = this.dataSource.getRepository(Deployment); | ||||||
|     const deployment = await deploymentRepository.save(data); | 
 | ||||||
|  |     const id = nanoid(); | ||||||
|  |     const url = `${data.project!.name}-${id}.${PROJECT_DOMAIN}`; | ||||||
|  | 
 | ||||||
|  |     const updatedData = { | ||||||
|  |       ...data, | ||||||
|  |       id, | ||||||
|  |       url | ||||||
|  |     }; | ||||||
|  |     const deployment = await deploymentRepository.save(updatedData); | ||||||
| 
 | 
 | ||||||
|     return deployment; |     return deployment; | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -19,10 +19,6 @@ export const main = async (): Promise<void> => { | |||||||
|   // TODO: get config path using cli
 |   // TODO: get config path using cli
 | ||||||
|   const { server, database, githubOauth } = await getConfig<Config>(DEFAULT_CONFIG_FILE_PATH); |   const { server, database, githubOauth } = await getConfig<Config>(DEFAULT_CONFIG_FILE_PATH); | ||||||
| 
 | 
 | ||||||
|   const db = new Database(database); |  | ||||||
|   await db.init(); |  | ||||||
|   const service = new Service(db); |  | ||||||
| 
 |  | ||||||
|   // TODO: Move to Service class
 |   // TODO: Move to Service class
 | ||||||
|   const app = new OAuthApp({ |   const app = new OAuthApp({ | ||||||
|     clientType: 'oauth-app', |     clientType: 'oauth-app', | ||||||
| @ -30,8 +26,12 @@ export const main = async (): Promise<void> => { | |||||||
|     clientSecret: githubOauth.clientSecret |     clientSecret: githubOauth.clientSecret | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   const db = new Database(database); | ||||||
|  |   await db.init(); | ||||||
|  |   const service = new Service(db, app); | ||||||
|  | 
 | ||||||
|   const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString(); |   const typeDefs = fs.readFileSync(path.join(__dirname, 'schema.gql')).toString(); | ||||||
|   const resolvers = await createResolvers(db, app, service); |   const resolvers = await createResolvers(service); | ||||||
| 
 | 
 | ||||||
|   await createAndStartServer(typeDefs, resolvers, server); |   await createAndStartServer(typeDefs, resolvers, server); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,10 +1,7 @@ | |||||||
| import debug from 'debug'; | import debug from 'debug'; | ||||||
| import { DeepPartial, FindOptionsWhere } from 'typeorm'; | import { DeepPartial, FindOptionsWhere } from 'typeorm'; | ||||||
| 
 | 
 | ||||||
| import { OAuthApp } from '@octokit/oauth-app'; |  | ||||||
| 
 |  | ||||||
| import { Service } from './service'; | import { Service } from './service'; | ||||||
| import { Database } from './database'; |  | ||||||
| import { Permission } from './entity/ProjectMember'; | import { Permission } from './entity/ProjectMember'; | ||||||
| import { Domain } from './entity/Domain'; | import { Domain } from './entity/Domain'; | ||||||
| import { Project } from './entity/Project'; | import { Project } from './entity/Project'; | ||||||
| @ -13,7 +10,7 @@ import { EnvironmentVariable } from './entity/EnvironmentVariable'; | |||||||
| const log = debug('snowball:database'); | const log = debug('snowball:database'); | ||||||
| 
 | 
 | ||||||
| // TODO: Remove Database argument and refactor code to Service
 | // TODO: Remove Database argument and refactor code to Service
 | ||||||
| export const createResolvers = async (db: Database, app: OAuthApp, service: Service): Promise<any> => { | export const createResolvers = async (service: Service): Promise<any> => { | ||||||
|   return { |   return { | ||||||
|     Query: { |     Query: { | ||||||
|       // TODO: add custom type for context
 |       // TODO: add custom type for context
 | ||||||
| @ -121,9 +118,9 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv | |||||||
|         } |         } | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       updateDeploymentToProd: async (_: any, { deploymentId }: { deploymentId: string }) => { |       updateDeploymentToProd: async (_: any, { deploymentId }: { deploymentId: string }, context: any) => { | ||||||
|         try { |         try { | ||||||
|           return await service.updateDeploymentToProd(deploymentId); |           return Boolean(await service.updateDeploymentToProd(context.userId, deploymentId)); | ||||||
|         } catch (err) { |         } catch (err) { | ||||||
|           log(err); |           log(err); | ||||||
|           return false; |           return false; | ||||||
| @ -201,19 +198,17 @@ export const createResolvers = async (db: Database, app: OAuthApp, service: Serv | |||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       authenticateGitHub: async (_: any, { code }: { code: string }, context: any) => { |       authenticateGitHub: async (_: any, { code }: { code: string }, context: any) => { | ||||||
|         // TOO: Move to Service class
 |         try { | ||||||
|         const { authentication: { token } } = await app.createToken({ |           return await service.authenticateGitHub(code, context.userId); | ||||||
|           code |         } catch (err) { | ||||||
|         }); |           log(err); | ||||||
| 
 |           return false; | ||||||
|         await db.updateUser(context.userId, { gitHubToken: token }); |         } | ||||||
| 
 |  | ||||||
|         return { token }; |  | ||||||
|       }, |       }, | ||||||
| 
 | 
 | ||||||
|       unauthenticateGitHub: async (_: any, __: object, context: any) => { |       unauthenticateGitHub: async (_: any, __: object, context: any) => { | ||||||
|         try { |         try { | ||||||
|           return db.updateUser(context.userId, { gitHubToken: null }); |           return service.unauthenticateGitHub(context.userId, { gitHubToken: null }); | ||||||
|         } catch (err) { |         } catch (err) { | ||||||
|           log(err); |           log(err); | ||||||
|           return false; |           return false; | ||||||
|  | |||||||
| @ -1,8 +1,8 @@ | |||||||
| import assert from 'assert'; | import assert from 'assert'; | ||||||
| import { customAlphabet } from 'nanoid'; |  | ||||||
| import { lowercase, numbers } from 'nanoid-dictionary'; |  | ||||||
| import { DeepPartial, FindOptionsWhere } from 'typeorm'; | import { DeepPartial, FindOptionsWhere } from 'typeorm'; | ||||||
| 
 | 
 | ||||||
|  | import { OAuthApp } from '@octokit/oauth-app'; | ||||||
|  | 
 | ||||||
| import { Database } from './database'; | import { Database } from './database'; | ||||||
| import { Deployment, Environment } from './entity/Deployment'; | import { Deployment, Environment } from './entity/Deployment'; | ||||||
| import { Domain } from './entity/Domain'; | import { Domain } from './entity/Domain'; | ||||||
| @ -11,15 +11,13 @@ import { Organization } from './entity/Organization'; | |||||||
| import { Project } from './entity/Project'; | import { Project } from './entity/Project'; | ||||||
| import { Permission, ProjectMember } from './entity/ProjectMember'; | import { Permission, ProjectMember } from './entity/ProjectMember'; | ||||||
| import { User } from './entity/User'; | import { User } from './entity/User'; | ||||||
| import { PROJECT_DOMAIN } from './constants'; |  | ||||||
| 
 |  | ||||||
| const nanoid = customAlphabet(lowercase + numbers, 8); |  | ||||||
| 
 |  | ||||||
| export class Service { | export class Service { | ||||||
|   private db: Database; |   private db: Database; | ||||||
|  |   private app: OAuthApp; | ||||||
| 
 | 
 | ||||||
|   constructor (db: Database) { |   constructor (db: Database, app: OAuthApp) { | ||||||
|     this.db = db; |     this.db = db; | ||||||
|  |     this.app = app; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async getUser (userId: string): Promise<User | null> { |   async getUser (userId: string): Promise<User | null> { | ||||||
| @ -149,7 +147,7 @@ export class Service { | |||||||
|     return this.db.deleteEnvironmentVariable(environmentVariableId); |     return this.db.deleteEnvironmentVariable(environmentVariableId); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async updateDeploymentToProd (deploymentId: string): Promise<boolean> { |   async updateDeploymentToProd (userId: string, deploymentId: string): Promise<Deployment> { | ||||||
|     const deployment = await this.db.getDeployment({ where: { id: deploymentId }, relations: { project: true } }); |     const deployment = await this.db.getDeployment({ where: { id: deploymentId }, relations: { project: true } }); | ||||||
| 
 | 
 | ||||||
|     if (!deployment) { |     if (!deployment) { | ||||||
| @ -173,13 +171,18 @@ export class Service { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const updateResult = await this.db.updateDeploymentById(deploymentId, { |     const { createdAt, updatedAt, ...updatedDeployment } = deployment; | ||||||
|       environment: Environment.Production, | 
 | ||||||
|       domain: prodBranchDomains[0], |     updatedDeployment.isCurrent = true; | ||||||
|       isCurrent: true |     updatedDeployment.environment = Environment.Production; | ||||||
|  |     updatedDeployment.domain = prodBranchDomains[0]; | ||||||
|  |     updatedDeployment.createdBy = Object.assign(new User(), { | ||||||
|  |       id: userId | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     return updateResult; |     const newDeployement = await this.db.addDeployement(updatedDeployment); | ||||||
|  | 
 | ||||||
|  |     return newDeployement; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   async addProject (userId: string, organizationSlug: string, data: DeepPartial<Project>): Promise<Project | undefined> { |   async addProject (userId: string, organizationSlug: string, data: DeepPartial<Project>): Promise<Project | undefined> { | ||||||
| @ -243,9 +246,6 @@ export class Service { | |||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     updatedDeployment.id = nanoid(); |  | ||||||
|     updatedDeployment.url = `${updatedDeployment.project.name}-${updatedDeployment.id}.${PROJECT_DOMAIN}`; |  | ||||||
| 
 |  | ||||||
|     const oldDeployment = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false }); |     const oldDeployment = await this.db.updateDeploymentById(deploymentId, { domain: null, isCurrent: false }); | ||||||
|     const newDeployement = await this.db.addDeployement(updatedDeployment); |     const newDeployement = await this.db.addDeployement(updatedDeployment); | ||||||
| 
 | 
 | ||||||
| @ -360,4 +360,18 @@ export class Service { | |||||||
| 
 | 
 | ||||||
|     return updateResult; |     return updateResult; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   async authenticateGitHub (code:string, userId: string): Promise<{token: string}> { | ||||||
|  |     const { authentication: { token } } = await this.app.createToken({ | ||||||
|  |       code | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     await this.db.updateUser(userId, { gitHubToken: token }); | ||||||
|  | 
 | ||||||
|  |     return { token }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   async unauthenticateGitHub (userId: string, data: DeepPartial<User>): Promise<boolean> { | ||||||
|  |     return this.db.updateUser(userId, data); | ||||||
|  |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -2,11 +2,11 @@ | |||||||
|   { |   { | ||||||
|     "id": "2379cf1f-a232-4ad2-ae14-4d881131cc26", |     "id": "2379cf1f-a232-4ad2-ae14-4d881131cc26", | ||||||
|     "name": "Snowball Tools", |     "name": "Snowball Tools", | ||||||
|     "slug": "snowball-tools" |     "slug": "snowball-tools-1" | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|     "id": "7eb9b3eb-eb74-4b53-b59a-69884c82a7fb", |     "id": "7eb9b3eb-eb74-4b53-b59a-69884c82a7fb", | ||||||
|     "name": "AirFoil", |     "name": "AirFoil", | ||||||
|     "slug": "airfoil" |     "slug": "airfoil-2" | ||||||
|   } |   } | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -9,7 +9,7 @@ import { | |||||||
|   Typography, |   Typography, | ||||||
| } from '@material-tailwind/react'; | } from '@material-tailwind/react'; | ||||||
| 
 | 
 | ||||||
| import { relativeTime } from '../../utils/time'; | import { relativeTimeISO } from '../../utils/time'; | ||||||
| import { ProjectDetails } from '../../types/project'; | import { ProjectDetails } from '../../types/project'; | ||||||
| 
 | 
 | ||||||
| interface ProjectCardProps { | interface ProjectCardProps { | ||||||
| @ -45,7 +45,7 @@ const ProjectCard: React.FC<ProjectCardProps> = ({ project }) => { | |||||||
|           {project.latestCommit.message} |           {project.latestCommit.message} | ||||||
|         </Typography> |         </Typography> | ||||||
|         <Typography variant="small" color="gray"> |         <Typography variant="small" color="gray"> | ||||||
|           {relativeTime(project.latestCommit.createdAt)} on{' '} |           {relativeTimeISO(project.latestCommit.createdAt)} on{' '} | ||||||
|           {project.latestCommit.branch} |           {project.latestCommit.branch} | ||||||
|         </Typography> |         </Typography> | ||||||
|       </div> |       </div> | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom'; | |||||||
| 
 | 
 | ||||||
| import { Chip, IconButton } from '@material-tailwind/react'; | import { Chip, IconButton } from '@material-tailwind/react'; | ||||||
| 
 | 
 | ||||||
| import { relativeTime } from '../../../utils/time'; | import { relativeTimeISO } from '../../../utils/time'; | ||||||
| import { GitRepositoryDetails } from '../../../types/project'; | import { GitRepositoryDetails } from '../../../types/project'; | ||||||
| import { useGQLClient } from '../../../context/GQLClientContext'; | import { useGQLClient } from '../../../context/GQLClientContext'; | ||||||
| 
 | 
 | ||||||
| @ -24,7 +24,6 @@ const ProjectRepoCard: React.FC<ProjectRepoCardProps> = ({ repository }) => { | |||||||
| 
 | 
 | ||||||
|     const { addProject } = await client.addProject(orgSlug!, { |     const { addProject } = await client.addProject(orgSlug!, { | ||||||
|       name: `${repository.owner!.login}-${repository.name}`, |       name: `${repository.owner!.login}-${repository.name}`, | ||||||
|       // TODO: Get organization id from context or URL
 |  | ||||||
|       prodBranch: repository.default_branch!, |       prodBranch: repository.default_branch!, | ||||||
|       repository: repository.full_name, |       repository: repository.full_name, | ||||||
|     }); |     }); | ||||||
| @ -50,7 +49,7 @@ const ProjectRepoCard: React.FC<ProjectRepoCardProps> = ({ repository }) => { | |||||||
|             /> |             /> | ||||||
|           )} |           )} | ||||||
|         </div> |         </div> | ||||||
|         <p>{repository.updated_at && relativeTime(repository.updated_at)}</p> |         <p>{repository.updated_at && relativeTimeISO(repository.updated_at)}</p> | ||||||
|       </div> |       </div> | ||||||
|       <div className="hidden group-hover:block"> |       <div className="hidden group-hover:block"> | ||||||
|         <IconButton size="sm">{'>'}</IconButton> |         <IconButton size="sm">{'>'}</IconButton> | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import React from 'react'; | |||||||
| 
 | 
 | ||||||
| import { Typography, IconButton } from '@material-tailwind/react'; | import { Typography, IconButton } from '@material-tailwind/react'; | ||||||
| 
 | 
 | ||||||
| import { relativeTime } from '../../../utils/time'; | import { relativeTimeISO } from '../../../utils/time'; | ||||||
| import { GitCommitDetails } from '../../../types/project'; | import { GitCommitDetails } from '../../../types/project'; | ||||||
| 
 | 
 | ||||||
| interface ActivityCardProps { | interface ActivityCardProps { | ||||||
| @ -17,7 +17,8 @@ const ActivityCard = ({ activity }: ActivityCardProps) => { | |||||||
|       <div className="grow"> |       <div className="grow"> | ||||||
|         <Typography>{activity.commit.author?.name}</Typography> |         <Typography>{activity.commit.author?.name}</Typography> | ||||||
|         <Typography variant="small" color="gray"> |         <Typography variant="small" color="gray"> | ||||||
|           {relativeTime(activity.commit.author!.date!)} ^ {activity.branch.name} |           {relativeTimeISO(activity.commit.author!.date!)} ^{' '} | ||||||
|  |           {activity.branch.name} | ||||||
|         </Typography> |         </Typography> | ||||||
|         <Typography variant="small" color="gray"> |         <Typography variant="small" color="gray"> | ||||||
|           {activity.commit.message} |           {activity.commit.message} | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ import React from 'react'; | |||||||
| import { Typography, Chip, Card } from '@material-tailwind/react'; | import { Typography, Chip, Card } from '@material-tailwind/react'; | ||||||
| import { color } from '@material-tailwind/react/types/components/chip'; | import { color } from '@material-tailwind/react/types/components/chip'; | ||||||
| import { DeploymentDetails } from '../../../../types/project'; | import { DeploymentDetails } from '../../../../types/project'; | ||||||
| import { relativeTime } from '../../../../utils/time'; | import { relativeTimeMs } from '../../../../utils/time'; | ||||||
| 
 | 
 | ||||||
| interface DeploymentDialogBodyCardProps { | interface DeploymentDialogBodyCardProps { | ||||||
|   deployment: DeploymentDetails; |   deployment: DeploymentDetails; | ||||||
| @ -28,14 +28,14 @@ const DeploymentDialogBodyCard = ({ | |||||||
|         /> |         /> | ||||||
|       )} |       )} | ||||||
|       <Typography variant="small" className="text-black"> |       <Typography variant="small" className="text-black"> | ||||||
|         {deployment.title} |         {deployment.url} | ||||||
|       </Typography> |       </Typography> | ||||||
|       <Typography variant="small"> |       <Typography variant="small"> | ||||||
|         ^ {deployment.branch} ^ {deployment.commitHash}{' '} |         ^ {deployment.branch} ^ {deployment.commitHash}{' '} | ||||||
|         {deployment.commit.message} |         {deployment.commit.message} | ||||||
|       </Typography> |       </Typography> | ||||||
|       <Typography variant="small"> |       <Typography variant="small"> | ||||||
|         ^ {relativeTime(deployment.updatedAt)} ^ {deployment.author} |         ^ {relativeTimeMs(deployment.createdAt)} ^ {deployment.createdBy.name} | ||||||
|       </Typography> |       </Typography> | ||||||
|     </Card> |     </Card> | ||||||
|   ); |   ); | ||||||
|  | |||||||
| @ -5,3 +5,6 @@ export const COMMIT_DETAILS = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const ORGANIZATION_ID = '2379cf1f-a232-4ad2-ae14-4d881131cc26'; | export const ORGANIZATION_ID = '2379cf1f-a232-4ad2-ae14-4d881131cc26'; | ||||||
|  | 
 | ||||||
|  | export const GIT_TEMPLATE_LINK = | ||||||
|  |   'https://git.vdb.to/cerc-io/test-progressive-web-app'; | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ import { Outlet, useLocation, useSearchParams } from 'react-router-dom'; | |||||||
| 
 | 
 | ||||||
| import Stepper from '../../../../components/Stepper'; | import Stepper from '../../../../components/Stepper'; | ||||||
| import templateDetails from '../../../../assets/templates.json'; | import templateDetails from '../../../../assets/templates.json'; | ||||||
|  | import { GIT_TEMPLATE_LINK } from '../../../../constants'; | ||||||
| 
 | 
 | ||||||
| const STEPPER_VALUES = [ | const STEPPER_VALUES = [ | ||||||
|   { step: 1, route: '/projects/create/template', label: 'Create repository' }, |   { step: 1, route: '/projects/create/template', label: 'Create repository' }, | ||||||
| @ -31,8 +32,11 @@ const CreateWithTemplate = () => { | |||||||
|       <div className="flex justify-between w-5/6 my-4 bg-gray-200 rounded-xl p-6"> |       <div className="flex justify-between w-5/6 my-4 bg-gray-200 rounded-xl p-6"> | ||||||
|         <div>^</div> |         <div>^</div> | ||||||
|         <div className="grow">{template?.name}</div> |         <div className="grow">{template?.name}</div> | ||||||
|         {/* TODO: Get template Git link from DB */} |         <div> | ||||||
|         <div>^snowball-tools/react-native-starter</div> |           <a href={GIT_TEMPLATE_LINK} target="_blank" rel="noreferrer"> | ||||||
|  |             cerc-io/test-progressive-web-app | ||||||
|  |           </a> | ||||||
|  |         </div> | ||||||
|       </div> |       </div> | ||||||
|       <div className="grid grid-cols-3 w-5/6 p-6"> |       <div className="grid grid-cols-3 w-5/6 p-6"> | ||||||
|         <div> |         <div> | ||||||
|  | |||||||
| @ -50,7 +50,6 @@ const CreateRepo = () => { | |||||||
| 
 | 
 | ||||||
|         const { addProject } = await client.addProject(orgSlug!, { |         const { addProject } = await client.addProject(orgSlug!, { | ||||||
|           name: `${gitRepo.data.owner!.login}-${gitRepo.data.name}`, |           name: `${gitRepo.data.owner!.login}-${gitRepo.data.name}`, | ||||||
|           // TODO: Get organization id from context or URL
 |  | ||||||
|           prodBranch: gitRepo.data.default_branch ?? 'main', |           prodBranch: gitRepo.data.default_branch ?? 'main', | ||||||
|           repository: gitRepo.data.full_name, |           repository: gitRepo.data.full_name, | ||||||
|         }); |         }); | ||||||
| @ -95,8 +94,6 @@ const CreateRepo = () => { | |||||||
|     } |     } | ||||||
|   }, [gitAccounts]); |   }, [gitAccounts]); | ||||||
| 
 | 
 | ||||||
|   // TODO: Get users and orgs from GitHub
 |  | ||||||
| 
 |  | ||||||
|   return ( |   return ( | ||||||
|     <form onSubmit={handleSubmit(submitRepoHandler)}> |     <form onSubmit={handleSubmit(submitRepoHandler)}> | ||||||
|       <div className="mb-2"> |       <div className="mb-2"> | ||||||
| @ -135,10 +132,7 @@ const CreateRepo = () => { | |||||||
|             name="account" |             name="account" | ||||||
|             control={control} |             control={control} | ||||||
|             render={({ field }) => ( |             render={({ field }) => ( | ||||||
|               <AsyncSelect |               <AsyncSelect {...field}> | ||||||
|                 {...field} |  | ||||||
|                 label={!field.value ? 'Select an account / Organization' : ''} |  | ||||||
|               > |  | ||||||
|                 {gitAccounts.map((account, key) => ( |                 {gitAccounts.map((account, key) => ( | ||||||
|                   <Option key={key} value={account}> |                   <Option key={key} value={account}> | ||||||
|                     ^ {account} |                     ^ {account} | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ import { DateTime } from 'luxon'; | |||||||
|  * @param {string} time - The input time in ISO 8601 format. |  * @param {string} time - The input time in ISO 8601 format. | ||||||
|  * @returns {string} - A human-readable relative time string. |  * @returns {string} - A human-readable relative time string. | ||||||
|  */ |  */ | ||||||
| export const relativeTime = (time: string) => { | export const relativeTimeISO = (time: string) => { | ||||||
|   return DateTime.fromISO(time).toRelative(); |   return DateTime.fromISO(time).toRelative(); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -17,5 +17,5 @@ export const relativeTime = (time: string) => { | |||||||
|  * @returns {string} - A human-readable relative time string. |  * @returns {string} - A human-readable relative time string. | ||||||
|  */ |  */ | ||||||
| export const relativeTimeMs = (time: string) => { | export const relativeTimeMs = (time: string) => { | ||||||
|   return relativeTime(new Date(Number(time)).toISOString()); |   return relativeTimeISO(new Date(Number(time)).toISOString()); | ||||||
| }; | }; | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user