@chakra-ui/react#GridItem TypeScript Examples
The following examples show how to use
@chakra-ui/react#GridItem.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: InventoryComponent.tsx From dope-monorepo with GNU General Public License v3.0 | 6 votes |
export default function InventoryComponent(props: InventoryProps) {
return (
<ChakraProvider>
<div style={inventoryBackgroundStyle}>
<Flex width="100%" height="100vh" gap={15}>
<Box width="35%" style={gridItemStyle}>
{
<Grid>
{props.quests.map((quest, index) => (
<GridItem key={index}>
<Box style={questItemStyle}>
<Text style={questNameStyle}>{quest.name}</Text>
</Box>
</GridItem>
))}
</Grid>
}
</Box>
<Box width="65%" style={gridItemStyle}></Box>
</Flex>
</div>
</ChakraProvider>
);
}
Example #2
Source File: WebhooksTable.tsx From bluebubbles-server with Apache License 2.0 | 5 votes |
WebhooksTable = ({ webhooks }: { webhooks: Array<WebhookItem> }): JSX.Element => {
const dispatch = useAppDispatch();
const dialogRef = useRef(null);
const [selectedId, setSelectedId] = useState(undefined as number | undefined);
return (
<Box>
<Table variant="striped" colorScheme="blue">
<TableCaption>These are callbacks to receive events from the BlueBubbles Server</TableCaption>
<Thead>
<Tr>
<Th>URL</Th>
<Th>Event Subscriptions</Th>
<Th isNumeric>Actions</Th>
</Tr>
</Thead>
<Tbody>
{webhooks.map(item => (
<Tr key={item.id}>
<Td>{item.url}</Td>
<Td>{JSON.parse(item.events).map((e: string) => webhookEventValueToLabel(e)).join(', ')}</Td>
<Td isNumeric>
<Grid templateColumns="repeat(2, 1fr)">
<Tooltip label='Edit' placement='bottom'>
<GridItem _hover={{ cursor: 'pointer' }} onClick={() => setSelectedId(item.id)}>
<Icon as={AiOutlineEdit} />
</GridItem>
</Tooltip>
<Tooltip label='Delete' placement='bottom'>
<GridItem _hover={{ cursor: 'pointer' }} onClick={() => dispatch(remove(item.id))}>
<Icon as={FiTrash} />
</GridItem>
</Tooltip>
</Grid>
</Td>
</Tr>
))}
</Tbody>
</Table>
<AddWebhookDialog
existingId={selectedId}
modalRef={dialogRef}
isOpen={!!selectedId}
onClose={() => {
setSelectedId(undefined);
}}
/>
</Box>
);
}
Example #3
Source File: Greeter.tsx From eth-dapps-nextjs-boiletplate with MIT License | 5 votes |
// REF: https://dev.to/jacobedawson/send-react-web3-dapp-transactions-via-metamask-2b8n
export default function Greeter() {
const { globalState, dispatch } = useContext(globalContext)
const { account, web3 } = globalState
const [greetingText, setGreetingText] = useState("")
const [greetingOutput, setGreetingOutput] = useState("")
const [greetingButtonLoading, greetingButton] = useButton(setGreeting, 'Set Greeting')
const [greeting, greetingInput] = useInput(greetingButtonLoading as boolean)
const [greetButtonLoading, greetButton] = useButton(handleGreet, 'Greet')
const [greet, greetInput] = useInput(greetButtonLoading as boolean)
const contractAddress = process.env.NEXT_PUBLIC_GREETER_CONTRACT_ADDRESS
const abiItems: AbiItem[] = web3 && GreeterContract.abi as AbiItem[]
const contract = web3 && contractAddress && new web3.eth.Contract(abiItems, contractAddress)
function getGreeting() {
console.log('getGreeting')
contract.methods.greeting().call().then((result: any) => {
setGreetingText(result)
});
}
async function handleGreet() {
console.log('handleGreet', greet)
try {
const result = await contract.methods.greet(greet).call()
setGreetingOutput(result)
} catch (error) {
console.error(error)
}
}
async function setGreeting() {
console.log('setGreeting')
try {
const result = await contract.methods.setGreeting(greeting).send({ from: account })
console.log('result', result)
getGreeting()
} catch (error) {
console.error('error in try...catch', error)
}
}
useEffect(() => {
if (contract) {
getGreeting()
}
})
return (
<div>
{
account && (
<Grid mt="5" templateColumns="repeat(2, 1fr)" templateRows="repeat(4, 1fr)" gap={3}>
<GridItem><Text textAlign="right" fontWeight="bold">Greeting</Text></GridItem>
<GridItem><Text>{greetingText}</Text></GridItem>
<GridItem alignItems="end">{greetingButton}</GridItem>
<GridItem>{greetingInput}</GridItem>
<GridItem alignItems="end">{greetButton}</GridItem>
<GridItem>{greetInput}</GridItem>
<GridItem colSpan={2}>
<Text fontWeight="bold" textAlign="center">{greetingOutput}</Text>
</GridItem>
</Grid>
)
}
</div>
)
}
Example #4
Source File: StatsLayout.tsx From calories-in with MIT License | 5 votes |
function StatsLayout({
nameElement,
amountElement,
energyElement,
proteinElement,
carbsElement,
fatElement,
menuElement,
forwardedRef,
prefersAmount = false,
...rest
}: Props) {
const screenSize = useScreenSize()
if (screenSize >= ScreenSize.Medium) {
return (
<Grid
ref={forwardedRef}
width="100%"
gap={0}
templateColumns="repeat(11, 1fr)"
{...rest}
>
{amountElement && <GridItem colSpan={2}>{amountElement}</GridItem>}
<GridItem colSpan={amountElement ? 4 : 6}>{nameElement}</GridItem>
<GridItem colSpan={1}>{energyElement}</GridItem>
<GridItem colSpan={1}>{proteinElement}</GridItem>
<GridItem colSpan={1}>{carbsElement}</GridItem>
<GridItem colSpan={1}>{fatElement}</GridItem>
<GridItem colSpan={1}>{menuElement}</GridItem>
</Grid>
)
}
if (screenSize === 1) {
return (
<Grid
ref={forwardedRef}
width="100%"
templateColumns="repeat(10, 1fr)"
gap={0}
{...rest}
>
{amountElement && <GridItem colSpan={2}>{amountElement}</GridItem>}
<GridItem colSpan={amountElement ? 4 : 6}>{nameElement}</GridItem>
<GridItem colSpan={2}>{energyElement}</GridItem>
<GridItem colSpan={2}>{menuElement}</GridItem>
</Grid>
)
}
return (
<Grid
ref={forwardedRef}
width="100%"
templateColumns="repeat(10, 1fr)"
gap={0}
{...rest}
>
{prefersAmount && <GridItem colSpan={3}>{amountElement}</GridItem>}
<GridItem colSpan={5}>{nameElement}</GridItem>
{!prefersAmount && <GridItem colSpan={3}>{energyElement}</GridItem>}
<GridItem colSpan={2}>{menuElement}</GridItem>
</Grid>
)
}
Example #5
Source File: index.tsx From dope-monorepo with GNU General Public License v3.0 | 4 votes |
Flex = () => {
const hustler = useHustler();
const router = useRouter();
const { account } = useWeb3React();
const [hustlerConfig, setHustlerConfig] = useState({} as Partial<HustlerCustomization>);
const [onChainImage, setOnChainImage] = useState('');
const { id: hustlerId } = router.query;
const [isOwnedByConnectedAccount, setIsOwnedByConnectedAccount] = useState(false);
// Check Contract see if this Hustler is owned by connected Account
useEffect(() => {
let isMounted = true;
if (hustler && account && hustlerId && isMounted) {
hustler.balanceOf(account, hustlerId.toString()).then(value => {
setIsOwnedByConnectedAccount(value.eq(1));
});
}
return () => {
isMounted = false;
};
}, [hustler, account, hustlerId]);
// Grab Hustler info from API
const { data, isFetching: isLoading } = useHustlerQuery(
{
where: {
id: String(hustlerId),
},
},
{
enabled: router.isReady && !!String(router.query.id),
},
);
// Set Hustler Config and SVG Image after data returns
useEffect(() => {
if (data?.hustlers?.edges?.[0]?.node) {
const h = data?.hustlers?.edges?.[0].node;
if (h?.svg) setOnChainImage(h?.svg);
setHustlerConfig({
...hustlerConfig,
name: h?.name || '',
title: h?.title || '',
sex: (h?.sex.toLowerCase() || 'male') as HustlerSex,
body: h.body?.id ? parseInt(h.body.id.split('-')[2]) : 0,
hair: h.hair?.id ? parseInt(h.hair.id.split('-')[2]) : 0,
facialHair: h.beard?.id ? parseInt(h.beard.id.split('-')[2]) : 0,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, hustlerId]);
const itemRles = useHustlerRles(data?.hustlers?.edges?.[0]?.node);
const items = useMemo<Item[]>(() => {
const hustler = data?.hustlers?.edges?.[0]?.node;
if (hustler) {
// Order matters
return [
hustler.weapon,
hustler.vehicle,
hustler.drug,
hustler.clothes,
hustler.hand,
hustler.waist,
hustler.foot,
hustler.neck,
hustler.ring,
hustler.accessory,
].filter(i => !!i) as Item[];
}
return [];
}, [data]);
useEffect(() => {
if (data?.hustlers?.edges?.[0]?.node) {
const h = data?.hustlers?.edges?.[0].node;
if (h?.svg) setOnChainImage(h?.svg);
setHustlerConfig({
...hustlerConfig,
name: h?.name || '',
title: h?.title || '',
bgColor: `#${h?.background}`,
sex: (h?.sex.toLowerCase() || 'male') as HustlerSex,
body: h.body?.id ? parseInt(h.body.id.split('-')[2]) : 0,
hair: h.hair?.id ? parseInt(h.hair.id.split('-')[2]) : 0,
facialHair: h.beard?.id ? parseInt(h.beard.id.split('-')[2]) : 0,
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, hustlerId]);
return (
<AppWindow padBody={true} navbar={<HustlerFlexNavBar />} scrollable>
<Head
title={`Dope Wars Hustler Flex`}
ogImage={`https://api.dopewars.gg/hustlers/${hustlerId}/sprites/composite.png`}
/>
{isLoading && <LoadingBlock />}
{!isLoading && itemRles && (
<Grid
templateColumns="repeat(auto-fit, minmax(240px, 1fr))"
gap="16px"
justifyContent="center"
alignItems="stretch"
width="100%"
padding="32px"
paddingTop="8px"
>
<PanelContainer
css={css`
grid-column: unset;
background-color: ${hustlerConfig.bgColor};
padding: 16px;
display: flex;
align-items: center;
${media.tablet`
grid-column: 1 / 3;
`}
`}
>
<Image src={onChainImage} alt={hustlerConfig.name} flex="1" />
</PanelContainer>
<PanelContainer>
<PanelBody>
<Grid
templateRows="2fr 1fr"
gap="8"
justifyContent="center"
alignItems="stretch"
width="100%"
>
<GridItem
display="flex"
justifyContent="center"
alignItems="flex-end"
paddingBottom="30px"
background="#000 url(/images/lunar_new_year_2022/explosion_city-bg.png) center / contain repeat-x"
overflow="hidden"
>
<HustlerSpriteSheetWalk id={hustlerId?.toString()} />
</GridItem>
<GridItem minWidth="256px">
<HustlerMugShot hustlerConfig={hustlerConfig} itemRles={itemRles} />
</GridItem>
</Grid>
</PanelBody>
</PanelContainer>
<PanelContainer>
<ProfileCardHeader>Equipped Gear</ProfileCardHeader>
<PanelBody
css={css`
background-color: var(--gray-800);
flex: 2;
`}
>
{items?.map(({ id, name, namePrefix, nameSuffix, suffix, augmented, type, tier }) => {
return (
<DopeItem
key={id}
name={name}
namePrefix={namePrefix}
nameSuffix={nameSuffix}
suffix={suffix}
augmented={augmented}
type={type}
color={DopeLegendColors[tier]}
isExpanded={true}
tier={tier}
showRarity={true}
/>
);
})}
</PanelBody>
</PanelContainer>
{items?.map(item => {
return (
<GearCard
item={item}
key={item.id}
showUnEquipFooter={isOwnedByConnectedAccount}
hustlerId={BigNumber.from(hustlerId)}
/>
);
})}
</Grid>
)}
</AppWindow>
);
}
Example #6
Source File: AppDomains.tsx From ledokku with MIT License | 4 votes |
AppDomains = ({ appId }: AppDomainProps) => {
const toast = useToast();
const { data, loading /* error */ } = useAppByIdQuery({
variables: {
appId,
},
ssr: false,
skip: !appId,
});
const {
data: domainsData,
loading: domainsDataLoading,
refetch: appDomainsRefetch,
} = useDomainsQuery({
variables: {
appId,
},
});
const [
removeDomainMutation,
{ loading: removeDomainMutationLoading },
] = useRemoveDomainMutation();
const handleRemoveDomain = async (domain: string) => {
try {
await removeDomainMutation({
variables: {
input: {
appId,
domainName: domain,
},
},
refetchQueries: [{ query: DomainsDocument, variables: { appId } }],
});
toast.success('Domain removed successfully');
} catch (error) {
toast.error(error.message);
}
};
if (!data) {
return null;
}
// // TODO display error
if (loading) {
// TODO nice loading
return <p>Loading...</p>;
}
const { app } = data;
if (!app) {
// TODO nice 404
return <p>App not found.</p>;
}
return (
<>
<Box py="5">
<Heading as="h2" size="md">
Domain management
</Heading>
<Text fontSize="sm" color="gray.400">
List of domains you have added to {app.name} app
</Text>
</Box>
<Grid templateColumns={{ sm: 'repeat(1, 1fr)', md: 'repeat(3, 1fr)' }}>
<GridItem colSpan={2}>
<Box mb="8">
{domainsDataLoading ? <Spinner /> : null}
{domainsData?.domains.domains.length === 0 ? (
<Text fontSize="sm" color="gray.400">
Currently you haven't added any custom domains to your app
</Text>
) : null}
{domainsData?.domains.domains.map((domain: any) => (
<Flex
key={domain}
justifyContent="space-between"
alignItems="center"
>
<Link
href={`http://${domain}`}
isExternal
display="flex"
alignItems="center"
>
{domain} <Icon as={FiExternalLink} mx="2" />
</Link>
<IconButton
aria-label="Delete"
variant="ghost"
colorScheme="red"
icon={<FiTrash2 />}
disabled={removeDomainMutationLoading}
onClick={() => handleRemoveDomain(domain)}
/>
</Flex>
))}
</Box>
<AddAppDomain appId={appId} appDomainsRefetch={appDomainsRefetch} />
</GridItem>
</Grid>
</>
);
}
Example #7
Source File: env.tsx From ledokku with MIT License | 4 votes |
EnvForm = ({ name, value, appId, isNewVar }: EnvFormProps) => {
const [inputType, setInputType] = useState('password');
const toast = useToast();
const [
setEnvVarMutation,
{ loading: setEnvVarLoading },
] = useSetEnvVarMutation();
const [
unsetEnvVarMutation,
{ loading: unsetEnvVarLoading },
] = useUnsetEnvVarMutation();
const handleDeleteEnvVar = async (event: any) => {
event.preventDefault();
try {
await unsetEnvVarMutation({
variables: { key: name, appId },
refetchQueries: [{ query: EnvVarsDocument, variables: { appId } }],
});
} catch (error) {
toast.error(error.message);
}
};
const formik = useFormik<{ name: string; value: string }>({
initialValues: {
name,
value,
},
onSubmit: async (values) => {
// TODO validate values
try {
await setEnvVarMutation({
variables: { key: values.name, value: values.value, appId },
refetchQueries: [{ query: EnvVarsDocument, variables: { appId } }],
});
if (isNewVar) {
formik.resetForm();
}
toast.success('Environment variable set successfully');
} catch (error) {
toast.error(error.message);
}
},
});
return (
//TODO Handle visual feedback on changing env
//TODO Provide infos about env vars
<form onSubmit={formik.handleSubmit} autoComplete="off">
<Grid
templateColumns={{ sm: 'repeat(1, 1fr)', md: 'repeat(3, 1fr)' }}
gap="3"
mt="3"
>
<GridItem>
<Input
autoComplete="off"
id={isNewVar ? 'newVarName' : name}
name="name"
placeholder="Name"
key={name}
value={formik.values.name}
onChange={formik.handleChange}
/>
</GridItem>
<GridItem>
<Input
autoComplete="off"
onMouseEnter={() => setInputType('text')}
onMouseLeave={() => setInputType('password')}
onFocus={() => setInputType('text')}
onBlur={() => setInputType('password')}
id={isNewVar ? 'newVarValue' : value}
name="value"
placeholder="Value"
key={value}
value={formik.values.value}
onChange={formik.handleChange}
type={inputType}
/>
</GridItem>
<GridItem display="flex">
<Button isLoading={setEnvVarLoading} type="submit">
{isNewVar ? 'Add' : 'Save'}
</Button>
{!isNewVar && (
<IconButton
aria-label="Delete"
variant="outline"
ml="3"
icon={<FiTrash2 />}
isLoading={unsetEnvVarLoading}
onClick={handleDeleteEnvVar}
/>
)}
</GridItem>
</Grid>
</form>
);
}
Example #8
Source File: advanced.tsx From ledokku with MIT License | 4 votes |
AppSettingsAdvanced = () => {
const { id: appId } = useParams<{ id: string }>();
const toast = useToast();
const history = useHistory();
const { data, loading } = useAppByIdQuery({
variables: {
appId,
},
});
const [
destroyAppMutation,
{ loading: destroyAppMutationLoading },
] = useDestroyAppMutation();
const DeleteAppNameSchema = yup.object().shape({
appName: yup
.string()
.required('Required')
.test(
'Equals app name',
'Must match app name',
(val) => val === data?.app?.name
),
});
const formik = useFormik<{ appName: string }>({
initialValues: {
appName: '',
},
validateOnChange: true,
validationSchema: DeleteAppNameSchema,
onSubmit: async (values) => {
try {
await destroyAppMutation({
variables: {
input: { appId },
},
refetchQueries: [
{
query: DashboardDocument,
},
],
});
toast.success('App deleted successfully');
history.push('/dashboard');
} catch (error) {
toast.error(error.message);
}
},
});
// TODO display error
if (loading) {
// TODO nice loading
return <p>Loading...</p>;
}
if (!data?.app) {
// TODO nice 404
return <p>App not found.</p>;
}
const { app } = data;
return (
<>
<HeaderContainer>
<Header />
<AppHeaderInfo app={app} />
<AppHeaderTabNav app={app} />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
<Grid
templateColumns={{ sm: 'repeat(1, 1fr)', md: 'repeat(6, 1fr)' }}
gap={{ sm: 0, md: 16 }}
>
<GridItem colSpan={2} py={5}>
<AppSettingsMenu app={app} />
</GridItem>
<GridItem colSpan={4}>
<AppRestart appId={app.id} />
<AppRebuild appId={app.id} />
<Box py="5">
<Heading as="h2" size="md">
Delete app
</Heading>
<Text fontSize="sm" color="gray.400">
This action cannot be undone. This will permanently delete{' '}
{app.name} app and everything related to it. Please type{' '}
<b>{app.name}</b> to confirm deletion.
</Text>
</Box>
<form onSubmit={formik.handleSubmit}>
<FormControl
id="appName"
isInvalid={Boolean(
formik.errors.appName && formik.touched.appName
)}
>
<Input
autoComplete="off"
id="appNme"
name="appName"
placeholder="App name"
value={formik.values.appName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>{formik.errors.appName}</FormErrorMessage>
</FormControl>
<Button
my={4}
type="submit"
colorScheme="red"
isLoading={destroyAppMutationLoading}
>
Delete
</Button>
</form>
</GridItem>
</Grid>
</Container>
</>
);
}
Example #9
Source File: create-app-dokku.tsx From ledokku with MIT License | 4 votes |
CreateAppDokku = () => {
const history = useHistory();
const toast = useToast();
const { data: dataApps } = useAppsQuery();
const [createAppDokkuMutation, { loading }] = useCreateAppDokkuMutation();
const createAppSchema = yup.object().shape({
name: yup
.string()
.required('App name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name exists',
'App with this name already exists',
(val) => !dataApps?.apps.find((app) => app.name === val)
),
});
const formik = useFormik<{
name: string;
}>({
initialValues: {
name: '',
},
validateOnChange: true,
validationSchema: createAppSchema,
onSubmit: async (values) => {
try {
const res = await createAppDokkuMutation({
variables: {
input: {
name: values.name,
},
},
});
trackGoal(trackingGoals.createAppDokku, 0);
if (res.data) {
history.push(`app/${res.data?.createAppDokku.appId}`);
toast.success('App created successfully');
}
} catch (error) {
toast.error(error);
}
},
});
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
<Heading as="h2" size="md">
Create a new app
</Heading>
<Text mt="12" mb="4" color="gray.400">
Enter app name, click create and voila!
</Text>
<Grid templateColumns={{ sm: 'repeat(1, 1fr)', md: 'repeat(3, 1fr)' }}>
<GridItem colSpan={2}>
<form onSubmit={formik.handleSubmit}>
<FormControl
id="v"
isInvalid={Boolean(formik.errors.name && formik.touched.name)}
>
<FormLabel>App name:</FormLabel>
<Input
autoComplete="off"
id="name"
name="name"
placeholder="Name"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>{formik.errors.name}</FormErrorMessage>
</FormControl>
<Box mt="4" display="flex" justifyContent="flex-end">
<Button
type="submit"
color="grey"
disabled={
loading || !formik.values.name || !!formik.errors.name
}
isLoading={loading}
>
Create
</Button>
</Box>
</form>
</GridItem>
</Grid>
</Container>
</>
);
}
Example #10
Source File: create-app-github.tsx From ledokku with MIT License | 4 votes |
CreateAppGithub = () => {
const history = useHistory();
const toast = useToast();
const { user } = useAuth();
const { data: dataApps } = useAppsQuery();
const [isNewWindowClosed, setIsNewWindowClosed] = useState(false);
const [selectedRepo, setSelectedRepo] = useState<Repository>();
const [selectedBranch, setSelectedBranch] = useState('');
const [isProceedModalOpen, setIsProceedModalOpen] = useState(false);
const {
data: installationData,
loading: installationLoading,
} = useGithubInstallationIdQuery({ fetchPolicy: 'network-only' });
const [
getRepos,
{ data: reposData, loading: reposLoading },
] = useRepositoriesLazyQuery({ fetchPolicy: 'network-only' });
const [
getBranches,
{ data: branchesData, loading: branchesLoading },
] = useBranchesLazyQuery({ fetchPolicy: 'network-only' });
const [arrayOfCreateAppLogs, setArrayOfCreateAppLogs] = useState<
RealTimeLog[]
>([]);
const [isTerminalVisible, setIsTerminalVisible] = useState(false);
const [isToastShown, setIsToastShown] = useState(false);
const [createAppGithubMutation, { loading }] = useCreateAppGithubMutation();
const [
isAppCreationSuccess,
setIsAppCreationSuccess,
] = useState<AppCreationStatus>();
useAppCreateLogsSubscription({
onSubscriptionData: (data) => {
const logsExist = data.subscriptionData.data?.appCreateLogs;
if (logsExist) {
setArrayOfCreateAppLogs((currentLogs) => {
return [...currentLogs, logsExist];
});
if (logsExist.type === 'end:success') {
setIsAppCreationSuccess(AppCreationStatus.SUCCESS);
} else if (logsExist.type === 'end:failure') {
setIsAppCreationSuccess(AppCreationStatus.FAILURE);
}
}
},
});
const createAppGithubSchema = yup.object().shape({
name: yup
.string()
.required('App name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name exists',
'App with this name already exists',
(val) => !dataApps?.apps.find((app) => app.name === val)
),
repo: yup.object({
fullName: yup.string().required(),
id: yup.string().required(),
name: yup.string().required(),
}),
installationId: yup.string().required(),
gitBranch: yup.string().optional(),
});
const formik = useFormik<{
name: string;
repo: {
fullName: string;
id: string;
name: string;
};
installationId: string;
gitBranch: string;
}>({
initialValues: {
name: '',
repo: {
fullName: '',
id: '',
name: '',
},
installationId: '',
gitBranch: '',
},
validateOnChange: true,
validationSchema: createAppGithubSchema,
onSubmit: async (values) => {
if (installationData) {
try {
await createAppGithubMutation({
variables: {
input: {
name: values.name,
gitRepoFullName: values.repo.fullName,
branchName: values.gitBranch,
gitRepoId: values.repo.id,
githubInstallationId: values.installationId,
},
},
});
setIsTerminalVisible(true);
} catch (error) {
error.message === 'Not Found'
? toast.error(`Repository : ${values.repo.fullName} not found`)
: toast.error(error.message);
}
}
},
});
const handleNext = () => {
setIsTerminalVisible(false);
const appId = arrayOfCreateAppLogs[arrayOfCreateAppLogs.length - 1].message;
history.push(`app/${appId}`, 'new');
trackGoal(trackingGoals.createAppGithub, 0);
};
const handleOpen = () => {
const newWindow = window.open(
`https://github.com/apps/${config.githubAppName}/installations/new`,
'Install App',
'resizable=1, scrollbars=1, fullscreen=0, height=1000, width=1020,top=' +
window.screen.width +
', left=' +
window.screen.width +
', toolbar=0, menubar=0, status=0'
);
const timer = setInterval(async () => {
if (newWindow && newWindow.closed) {
setIsNewWindowClosed(true);
clearInterval(timer);
}
}, 100);
};
useEffect(() => {
if (!installationLoading && installationData && isNewWindowClosed) {
getRepos({
variables: {
installationId: installationData.githubInstallationId.id,
},
});
setIsNewWindowClosed(false);
}
}, [
installationData,
installationLoading,
isNewWindowClosed,
setIsNewWindowClosed,
getRepos,
]);
useEffect(() => {
if (
!installationLoading &&
installationData &&
!reposLoading &&
reposData &&
selectedRepo
) {
getBranches({
variables: {
installationId: installationData.githubInstallationId.id,
repositoryName: selectedRepo.name,
},
});
}
}, [
installationData,
installationLoading,
reposData,
reposLoading,
getBranches,
selectedRepo?.name,
selectedRepo,
]);
const handleChangeRepo = (active: RepoOption) => {
setSelectedRepo(active.value);
setSelectedBranch('');
if (installationData) {
formik.setValues({
name: active.value.name,
installationId: installationData?.githubInstallationId.id,
repo: {
fullName: active.value.fullName,
name: active.value.name,
id: active.value.id,
},
gitBranch: '',
});
}
};
const handleChangeBranch = (active: BranchOption) => {
setSelectedBranch(active.value.name);
formik.setFieldValue('gitBranch', active.value.name);
};
const repoOptions: RepoOption[] = [];
if (reposData && !reposLoading) {
reposData?.repositories.map((r) =>
repoOptions.push({ value: r, label: r.fullName })
);
}
let branchOptions: BranchOption[] = [];
if (branchesData && !branchesLoading) {
branchesData.branches.map((b) =>
branchOptions.push({ value: b, label: b.name })
);
}
useEffect(() => {
if (installationData && !installationLoading) {
getRepos({
variables: {
installationId: installationData?.githubInstallationId.id,
},
});
}
}, [installationLoading, getRepos, installationData]);
useEffect(() => {
if (selectedRepo && installationData) {
getBranches({
variables: {
installationId: installationData?.githubInstallationId.id,
repositoryName: selectedRepo.name,
},
});
}
}, [selectedRepo, getBranches, installationData]);
// Effect for app creation
useEffect(() => {
isAppCreationSuccess === AppCreationStatus.FAILURE && !isToastShown
? toast.error('Failed to create an app') && setIsToastShown(true)
: isAppCreationSuccess === AppCreationStatus.SUCCESS &&
!isToastShown &&
toast.success('App created successfully') &&
setIsToastShown(true);
}, [isToastShown, isAppCreationSuccess, toast]);
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" mt={10}>
{isTerminalVisible ? (
<>
<p className="mb-2 ">
Creating <b>{formik.values.name}</b> app from{' '}
<b>{formik.values.repo.name}</b>
</p>
<p className="text-gray-500 mb-2">
Creating app usually takes a couple of minutes. Breathe in,
breathe out, logs are about to appear below:
</p>
<Terminal className={'w-6/6'}>
{arrayOfCreateAppLogs.map((log) => (
<p
key={arrayOfCreateAppLogs.indexOf(log)}
className={'text-s leading-5'}
>
{log.message?.replaceAll('[1G', '')}
</p>
))}
</Terminal>
{!!isAppCreationSuccess &&
isAppCreationSuccess === AppCreationStatus.SUCCESS ? (
<div className="mt-12 flex justify-end">
<Button
onClick={() => handleNext()}
color="grey"
iconEnd={<FiArrowRight size={20} />}
>
Next
</Button>
</div>
) : !!isAppCreationSuccess &&
isAppCreationSuccess === AppCreationStatus.FAILURE ? (
<div className="mt-12 flex justify-start">
<Button
onClick={() => {
setIsTerminalVisible(false);
formik.resetForm();
}}
color="grey"
iconEnd={<FiArrowLeft size={20} />}
>
Back
</Button>
</div>
) : null}
</>
) : (
<>
<Heading as="h2" size="md">
Create a new GitHub application
</Heading>
{installationData &&
!installationLoading &&
reposData &&
!reposLoading ? (
<>
<Text color="gray.400">
When you push to Git, your application will be redeployed
automatically.
</Text>
<Grid
templateColumns={{
sm: 'repeat(1, 1fr)',
md: 'repeat(3, 1fr)',
}}
>
<GridItem colSpan={2}>
<Flex alignItems="center" mt="12">
<Avatar
size="sm"
name={user?.userName}
src={user?.avatarUrl}
/>
<Text ml="2" fontWeight="bold">
{user?.userName}
</Text>
</Flex>
<form onSubmit={formik.handleSubmit}>
<Box mt="8">
<FormLabel>Repository</FormLabel>
<Select
placeholder="Select repository"
isSearchable={false}
onChange={handleChangeRepo}
options={repoOptions}
/>
</Box>
<Text mt="1" color="gray.400" fontSize="sm">
Can't see your repo in the list?{' '}
<Link
onClick={() => setIsProceedModalOpen(true)}
textDecoration="underline"
>
Configure the GitHub app.
</Link>
</Text>
<Box mt="8">
<FormLabel>Branch to deploy</FormLabel>
<Select
placeholder="Select branch"
isSearchable={false}
disabled={
!branchesData ||
branchesLoading ||
reposLoading ||
!reposData
}
onChange={handleChangeBranch}
options={branchOptions}
/>
</Box>
<Box mt="8" display="flex" justifyContent="flex-end">
<Button
type="submit"
color="grey"
disabled={!selectedBranch || !selectedRepo}
isLoading={loading}
>
Create
</Button>
</Box>
</form>
</GridItem>
</Grid>
</>
) : !reposLoading && !installationLoading && !reposData ? (
<>
<Alert mb="4" mt="4" w="65%" status="info">
<AlertIcon />
<Box flex="1">
<AlertTitle>Set up repository permissions</AlertTitle>
<AlertDescription display="block">
First you will need to set up permissions for repositories
that you would like to use with Ledokku. Once that's done,
it's time to choose repo and branch that you would like to
create app from and off we go.
</AlertDescription>
</Box>
</Alert>
<Button
color="grey"
onClick={() => setIsProceedModalOpen(true)}
>
Set up permissions
</Button>
</>
) : (
<Spinner />
)}
</>
)}
<Modal
isOpen={isProceedModalOpen}
onClose={() => setIsProceedModalOpen(false)}
isCentered
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Github setup info</ModalHeader>
<ModalCloseButton />
<ModalBody>
New window is about to open. After you are done selecting github
repos, close the window and refresh page.
</ModalBody>
<ModalFooter>
<Button
color="grey"
variant="outline"
className="mr-3"
onClick={() => setIsProceedModalOpen(false)}
>
Cancel
</Button>
<Button
color="grey"
onClick={() => {
handleOpen();
setIsProceedModalOpen(false);
}}
>
Proceed
</Button>
</ModalFooter>
</ModalContent>
</Modal>
</Container>
</>
);
}
Example #11
Source File: dashboard.tsx From ledokku with MIT License | 4 votes |
Dashboard = () => {
// const history = useHistory();
const { data /* loading, error */ } = useDashboardQuery({
fetchPolicy: 'cache-and-network',
});
// TODO show loading
// TODO handle error
// TODO if no apps or dbs show onboarding screen
return (
<div>
<HeaderContainer>
<Header />
<HomeHeaderTabNav />
</HeaderContainer>
<Container maxW="5xl" py={6}>
<Box display="flex" justifyContent="flex-end" pb={6}>
<Link to="/create-database">
<Button colorScheme="gray" variant="outline" fontSize="sm" mr={3}>
Create database
</Button>
</Link>
<Link to="/create-app">
<Button colorScheme="gray" fontSize="sm">
Create app
</Button>
</Link>
</Box>
<Grid
as="main"
templateColumns="repeat(12, 1fr)"
gap={{ base: 6, md: 20 }}
pt={4}
>
<GridItem colSpan={{ base: 12, md: 7 }}>
<Heading as="h2" size="md" py={5}>
Apps
</Heading>
{data?.apps.length === 0 ? (
<Text fontSize="sm" color="gray.400">
No apps deployed.
</Text>
) : null}
{data?.apps.map((app) => (
<Box
key={app.id}
py={3}
borderBottom={'1px'}
borderColor="gray.200"
>
<Box mb={1} color="gray.900" fontWeight="medium">
<Link to={`/app/${app.id}`}>{app.name}</Link>
</Box>
<Box
fontSize="sm"
color="gray.400"
display="flex"
justifyContent="space-between"
>
<Text display="flex" alignItems="center">
<Box mr={1} as="span">
{app.appMetaGithub ? (
<GithubIcon size={16} />
) : (
<Image
boxSize="16px"
objectFit="cover"
src="/dokku.png"
alt="dokkuLogo"
/>
)}
</Box>
{app.appMetaGithub
? `${app.appMetaGithub.repoOwner}/${app.appMetaGithub.repoName}`
: ''}
</Text>
<Text>
Created on {format(new Date(app.createdAt), 'MM/DD/YYYY')}
</Text>
</Box>
</Box>
))}
<Heading as="h2" size="md" py={5} mt={8}>
Databases
</Heading>
{data?.databases.length === 0 ? (
<Text fontSize="sm" color="gray.400">
No databases created.
</Text>
) : null}
{data?.databases.map((database) => {
const DbIcon = dbTypeToIcon(database.type);
return (
<Box
key={database.id}
py={3}
borderBottom={'1px'}
borderColor="gray.200"
>
<Box mb={1} color="gray.900" fontWeight="medium">
<Link to={`/database/${database.id}`}>{database.name}</Link>
</Box>
<Box
fontSize="sm"
color="gray.400"
display="flex"
justifyContent="space-between"
>
<Text display="flex" alignItems="center">
<Box mr={1} as="span">
<DbIcon size={16} />
</Box>
{dbTypeToReadableName(database.type)}
</Text>
<Text>
Created on{' '}
{format(new Date(database.createdAt), 'MM/DD/YYYY')}
</Text>
</Box>
</Box>
);
})}
</GridItem>
<GridItem colSpan={{ base: 12, md: 5 }}>
<Heading as="h2" size="md" py={5}>
Latest activity
</Heading>
<Text fontSize="sm" color="gray.400">
Coming soon
</Text>
</GridItem>
</Grid>
</Container>
</div>
);
}