@chakra-ui/react#Grid TypeScript Examples
The following examples show how to use
@chakra-ui/react#Grid.
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: FormControl.tsx From openchakra with MIT License | 6 votes |
FormControl: React.FC<FormControlPropType> = ({ label, htmlFor, children, hasColumn, }) => ( <ChakraFormControl mb={3} as={Grid} display="flex" alignItems="center" justifyItems="center" > <FormLabel p={0} mr={2} color="gray.500" lineHeight="1rem" width={hasColumn ? '2.5rem' : '90px'} fontSize="xs" htmlFor={htmlFor} > {label} </FormLabel> <Box display="flex" alignItems="center" justifyItems="center" width={hasColumn ? '30px' : '130px'} > {children} </Box> </ChakraFormControl> )
Example #2
Source File: Recommend.tsx From phonepare with MIT License | 6 votes |
Recommend : FC = () => {
const [ questionIndex, setQuestionIndex ] = useRecoilState(questionIndexState)
const [ questionTags, setQuestionTags ] = useRecoilState(questionTagsState)
const setSelecetedPhones = useSetRecoilState(selectedPhonesState)
return <Box py={10} textAlign='center'>
<Heading mb={3}>폰 추천받기</Heading>
{
questionIndex < Questions.length ? <Box px={{ base: 10, md: 32 }}>
<Flex height='60vh' alignItems='center' justifyContent='center'>
<Box>
<Heading fontSize='2xl'>{Questions[questionIndex].question}</Heading>
<Progress mt={4} mb={6} value={questionIndex} max={Questions.length} size='md' rounded='2xl' />
<Grid templateColumns={{ base: 'repeat(1, 1fr)', md: 'repeat(2, 1fr)' }} gap={5}>
{
Questions[questionIndex].options.map((option, index) => <Card key={index} onClick={() => {
setQuestionTags({ ...questionTags, [Questions[questionIndex].id]: option.value })
setQuestionIndex(questionIndex + 1)
if(questionIndex < Questions.length) setSelecetedPhones(getRecommended(questionTags).map(x => x.data.id) as [ string, string, string ])
}}>
<Text>{option.subLabel}</Text>
<Heading fontSize='xl'>{option.label}</Heading>
</Card>
)
}
</Grid>
</Box>
</Flex>
</Box> : <Box>
<Heading fontSize='2xl'>아래 휴대폰을 추천드립니다!</Heading>
<Button mt={4} onClick={() => setQuestionIndex(0)}>다시 고르기</Button>
<Compare asComponent />
</Box>
}
</Box>
}
Example #3
Source File: Index.tsx From phonepare with MIT License | 6 votes |
Index: FC = () => {
return <Box minHeight='100vh'>
<Flex height='100vh' alignItems='center' justifyContent='center'>
<Box textAlign='center'>
<Heading mb={10}>어떤 메뉴를 찾으시나요?</Heading>
<Grid templateColumns={{ base: 'repeat(1, 1fr)', md: 'repeat(2, 1fr)' }} gap={5}>
<Link to='/compare'>
<Card>
<Text>전 휴대폰을 잘 알아요!</Text>
<Heading fontSize='2xl'>폰 비교하기</Heading>
</Card>
</Link>
<Link to='/recommend'>
<Card>
<Text>저에게 맞는 휴대폰을 찾아보고 싶어요!</Text>
<Heading fontSize='2xl'>폰 추천받기</Heading>
</Card>
</Link>
</Grid>
</Box>
</Flex>
</Box>
}
Example #4
Source File: Spec.tsx From phonepare with MIT License | 6 votes |
Spec: FC = () => {
const phones = useRecoilValue(selectedPhonesDataState)
return <Box>
{
<PhoneGrid>
{
phones.map((phone, index) => {
return <Box key={index}>
{
phone ? <Grid gridRowGap={4}>
<Information name='프로세서' data={phone.data.cpu} icon={MdMemory} />
<Information name='메모리' data={phone.data.ram} icon={FaMemory} />
<Information name='출시일' data={phone.data.releaseDate} icon={FaCalendarDay} />
</Grid>
: ''
}
</Box>
}
)}
</PhoneGrid>
}
</Box>
}
Example #5
Source File: Size.tsx From phonepare with MIT License | 6 votes |
Size: FC = () => {
const phones = useRecoilValue(selectedPhonesDataState)
return <Box>
{
<PhoneGrid>
{
phones.map((phone, index) => {
return <Box key={index}>
{
phone ? <Grid gridRowGap={4}>
<Information name='크기 (가로 x 세로 x 높이)' data={`${phone.data.size}mm`} />
<Information name='무게' data={`${phone.data.weight}g`} />
</Grid>
: ''
}
</Box>
}
)}
</PhoneGrid>
}
</Box>
}
Example #6
Source File: Others.tsx From phonepare with MIT License | 6 votes |
Others: FC = () => {
const phones = useRecoilValue(selectedPhonesDataState)
return <Box>
{
<PhoneGrid>
{
phones.map((phone, index) => {
return <Box key={index}>
{
phone ? <Grid gridRowGap={4}>
<Information name='방수 등급' data={phone.data.waterproof} />
</Grid>
: ''
}
</Box>
}
)}
</PhoneGrid>
}
</Box>
}
Example #7
Source File: Display.tsx From phonepare with MIT License | 6 votes |
Display: FC = () => {
const phones = useRecoilValue(selectedPhonesDataState)
return <Box>
{
<PhoneGrid>
{
phones.map((phone, index) => {
return <Box key={index}>
{
phone ? <Grid gridRowGap={4}>
<Box textAlign='center'>
<Text fontSize='7xl' fontWeight='semibold'>{phone.data.display.size}"</Text>
<Text fontSize='2xl' mt={-4}>{phone.data.display.type}</Text>
</Box>
<Information name='해상도' data={phone.data.display.resolution} />
<Information name='PPI' data={String(phone.data.display.ppi)} />
</Grid>
: ''
}
</Box>
}
)}
</PhoneGrid>
}
</Box>
}
Example #8
Source File: Camera.tsx From phonepare with MIT License | 6 votes |
Camera: FC = () => {
const phones = useRecoilValue(selectedPhonesDataState)
return <Box>
{
<PhoneGrid>
{
phones.map((phone, index) => {
return <Box key={index}>
{
phone ? <Grid gridRowGap={4}>
<Information name='전면 카메라' data={joinWithBr(phone.data.camera.front)} />
<Information name='후면 카메라' data={joinWithBr(phone.data.camera.back)} />
</Grid>
: ''
}
</Box>
}
)}
</PhoneGrid>
}
</Box>
}
Example #9
Source File: Battery.tsx From phonepare with MIT License | 6 votes |
Battery: FC = () => {
const phones = useRecoilValue(selectedPhonesDataState)
return <Box>
{
<PhoneGrid>
{
phones.map((phone, index) => {
return <Box key={index}>
{
phone ? <Grid gridRowGap={4}>
<Information name='배터리 용량' data={`${phone.data.battery}mAh`} />
<Information name='충전 방식' data={phone.data.charge} />
</Grid>
: ''
}
</Box>
}
)}
</PhoneGrid>
}
</Box>
}
Example #10
Source File: learning.tsx From website with MIT License | 6 votes |
LearningPage: NextPage<LearningPageProps> = ({ featuredMaterials, theRest }) => (
<Page title="Learning">
<Content>
<Box as="section" backgroundColor="reactBlue.100" px={[4, null, null, 8]} py={['3.1em', null, null, '6.1em']}>
<Grid gap="2px" justifyContent="center" gridAutoFlow="row" textAlign="center" mx="auto" maxWidth="6xl">
<Heading as="span" fontFamily="body" fontWeight={300} mb={2} textTransform="uppercase" size="md" textAlign="center">
Ingin Belajar React?
</Heading>
<Heading as="h1" fontFamily="body" fontWeight={600} size="2xl" textAlign="center">
Materi Pembelajaran
</Heading>
<Text as="h2" mt="20px" mb="16px" fontSize={16} textAlign="center">
Beberapa konsep React memang terlihat janggal, tapi di luar itu React sangat mudah untuk dipelajari dan dipahami, baik mereka
yang sudah mahir dalam JavaScript modern ataupun yang baru saja memulai. Cobalah memulai dari salah satu materi di bawah.
</Text>
<Grid templateColumns="repeat(auto-fit, minmax(calc(296px), 1fr))" gap="24px" mt="36px">
{featuredMaterials.map(item => (
<LearningCard heading={item.type} title={item.title} desc={item.description} href={item.url} key={item.title} />
))}
{theRest.map(item => (
<LearningCard heading={item.type} title={item.title} desc={item.description} href={item.url} key={item.title} />
))}
</Grid>
</Grid>
</Box>
</Content>
</Page>
)
Example #11
Source File: Footer.tsx From website with MIT License | 6 votes |
Footer: React.FC<FooterProps> = ({ className }) => (
<Grid
as="footer"
px="24px"
py="36px"
color="gray.300"
bg="gray.900"
templateColumns="1fr 1fr minmax(auto, 1140px) 1fr 1fr"
className={className}
>
<Stack align="center" justify="center" gridColumn="3/4" textAlign="center">
<section>
<Text margin="0" lineHeight="20px" fontSize="12px">
© 2020 ReactJS ID.
</Text>
<Text margin="0" lineHeight="20px" fontSize="12px">
Kode sumber situs ini tersedia di{' '}
<Link href="https://github.com/reactjs-id/website" isExternal color="#fff" rel="noopener noreferrer">
GitHub
</Link>
. Gambar latar disediakan oleh{' '}
<Link href="https://www.transparenttextures.com/" isExternal color="#fff" rel="noopener noreferrer">
Transparent Textures
</Link>
.
</Text>
</section>
</Stack>
</Grid>
)
Example #12
Source File: IconList.tsx From lucide with ISC License | 6 votes |
IconList = memo(({ icons }: IconListProps) => {
const router = useRouter();
return (
<Grid templateColumns={`repeat(auto-fill, minmax(150px, 1fr))`} gap={5} marginBottom="320px">
{icons.map(icon => {
return (
<Link
key={icon.name}
scroll={false}
shallow={true}
href={{
pathname: '/icon/[iconName]',
query: {
...router.query,
iconName: icon.name,
},
}}
>
<IconListItem {...icon} />
</Link>
);
})}
</Grid>
);
})
Example #13
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 #14
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 #15
Source File: index.tsx From dendron with GNU Affero General Public License v3.0 | 5 votes |
export default function Home() {
const { isError, gardens, error } = useDendronGardens();
if (isError) return <div>failed to load: {JSON.stringify(error)}</div>;
let extra: any;
// if (_.isEmpty(gardens)) {
// extra = <Button>New Garden from Git</Button>;
// }
if (_.isEmpty(gardens)) {
extra = (
<Box maxW="32rem">
<VStack spacing={4} align="stretch">
<Center>
<Heading mb={4}>Welcome to Dendron</Heading>
</Center>
<Text fontSize="xl">
If you haven't already done so, you can install Dendron by following
the instructions
<Link
href="https://dendron.so/notes/678c77d9-ef2c-4537-97b5-64556d6337f1.html"
isExternal
>
here
</Link>
</Text>
<Button>Publish a new site from Git (coming soon) </Button>
</VStack>
</Box>
);
}
return (
<>
<Head>
<title>Dendron</title>
<link rel="icon" href="/favicon.ico" />
<script type="text/javascript" src="/static/memberful.js" />
</Head>
<Grid justifyContent="center">{extra}</Grid>
</>
);
}
Example #16
Source File: PhoneGrid.tsx From phonepare with MIT License | 5 votes |
PhoneGrid: FC<{ children: ReactNode[] }> = ({ children }) => {
const [ largeScreen ] = useMediaQuery('(min-width: 62em)')
return <Grid templateColumns={{ base: 'repeat(2, 1fr)', lg: 'repeat(3, 1fr)' }} columnGap={10}>
{children.slice(0, largeScreen ? 3 : 2)}
</Grid>
}
Example #17
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 #18
Source File: index.tsx From website with MIT License | 5 votes |
IndexPage: NextPage = () => (
<Page>
<Content>
<HomePageHeader
welcomeMessage="Selamat Datang"
title="Komunitas Developer ReactJS Indonesia"
desc="ReactJS ID adalah komunitas para developer React dan React Native. Kami mengadakan ajang meetup setiap bulannya, dimana para developer React bertukar informasi mengenai React dan ekosistemnya."
/>
<Box backgroundColor="#f2f2f2" px={[4, null, null, 8]} py={['3.1em', null, null, '6.1em']}>
<Box mx="auto" maxWidth="6xl">
<Grid gridTemplateColumns={['1fr', null, null, '1fr 1fr']} gridGap={8} alignItems="center">
<Img
display={['none', null, null, 'block']}
gridColumn={1}
width="400px"
height="400px"
src="/rumah-komunitas/tshirt.jpg"
alt="tshirt from rumah komunitas"
/>
<Grid gridColumn={[1, null, null, 2]}>
<Heading as="h2" mb={2} size="md" textTransform="uppercase" fontWeight={300}>
Kabar Gembira!
</Heading>
<Heading as="h3" size="2xl" fontWeight={600}>
Merchandise Resmi ReactJS Indonesia
</Heading>
<Text as="p" my="18px">
Merchandise resmi ReactJS Indonesia kini tersedia berkat kerjasama oleh Rumah Komunitas. Klik link di bawah untuk
mendapatkan T-shirt dan jaket ReactJS Indonesia.
</Text>
<LinkButton ml="-16px" href="https://www.rumahkomunitas.com/react-indonesia" isExternal>
Dapatkan Segera
</LinkButton>
</Grid>
</Grid>
</Box>
</Box>
<Box backgroundColor="reactBlue.100" px={[4, null, null, 8]} py={['3.1em', null, null, '6.1em']}>
<Grid gap="2px" justifyContent="center" gridAutoFlow="row" textAlign="center" mx="auto" maxWidth="6xl">
<Heading as="h2" mb={2} color="gray.900" size="md" textTransform="uppercase" fontWeight={300}>
Ingin Belajar React?
</Heading>
<Heading as="h3" color="gray.900" size="2xl" fontWeight={600}>
Materi Pembelajaran
</Heading>
<Text as="p" color="gray.900" my="1.2em">
Beberapa konsep React memang terlihat janggal, tapi diluar itu React sangat mudah untuk dipelajari dan dipahami, baik mereka
yang sudah mahir dalam JavaScript modern ataupun yang baru saja memulai. Cobalah memulai dari salah satu materi di bawah.
</Text>
<Grid gridTemplateColumns="repeat(auto-fit, minmax(calc(296px), 1fr))" gap="24px" mt="24px">
{learningResources
.filter(resource => resource.featured)
.map(item => (
<LearningCard heading={item.type} title={item.title} desc={item.description} href={item.url} key={item.title} />
))}
</Grid>
<Flex justifyContent="center" mt="4em">
<NextLink href="/learning">
<LinkButton _hover={{ backgroundColor: 'reactBlue.900' }} mt="auto" backgroundColor="reactBlue.800" color="white">
Lihat Selengkapnya
</LinkButton>
</NextLink>
</Flex>
</Grid>
</Box>
</Content>
</Page>
)
Example #19
Source File: HuesPickerControl.tsx From openchakra with MIT License | 5 votes |
HuesPickerControl = (props: HuesPickerPropType) => {
const [hue, setHue] = useState(500)
return (
<>
<Grid mb={2} templateColumns="repeat(5, 1fr)" gap={0}>
{Object.keys(props.themeColors).map(colorName =>
props.gradient ? (
<Box
border={colorName.includes('white') ? '1px solid lightgrey' : ''}
key={colorName}
_hover={{ boxShadow: 'lg' }}
cursor="pointer"
bg={`${colorName}.${props.enableHues ? hue : 500}`}
onClick={() => {
if (props.updateGradient) {
props.updateGradient(`${colorName}.${hue}`, props.index!)
}
}}
mt={2}
borderRadius="full"
height="30px"
width="30px"
/>
) : (
<Box
border={colorName.includes('white') ? '1px solid lightgrey' : ''}
key={colorName}
_hover={{ boxShadow: 'lg' }}
cursor="pointer"
bg={`${colorName}.${props.enableHues ? hue : 500}`}
onClick={() =>
props.setValue(
props.name,
props.enableHues ? `${colorName}.${hue}` : colorName,
)
}
mt={2}
borderRadius="full"
height="30px"
width="30px"
/>
),
)}
</Grid>
{props.enableHues && (
<>
<Slider
onChange={value => {
value = value === 0 ? 50 : value
setHue(value)
}}
min={0}
max={900}
step={100}
value={hue}
>
<SliderTrack>
<SliderFilledTrack />
</SliderTrack>
<SliderThumb boxSize={8}>
<Box borderRadius="full" fontSize="xs">
{hue}
</Box>
</SliderThumb>
</Slider>
</>
)}
</>
)
}
Example #20
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 #21
Source File: IconCustomizerDrawer.tsx From lucide with ISC License | 5 votes |
export function IconCustomizerDrawer() {
const [showCustomize, setShowCustomize] = useState(false);
const { color, setColor, size, setSize, strokeWidth, setStroke, resetStyle } = useContext(IconStyleContext);
return (
<>
<Button as="a" leftIcon={<Edit />} size="lg" onClick={() => setShowCustomize(true)}>
Customize
</Button>
<Drawer isOpen={showCustomize} placement="right" onClose={() => setShowCustomize(false)}>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Customize Icons</DrawerHeader>
<DrawerBody>
<Grid gridGap={'1em'}>
<FormControl>
<ColorPicker
color={color}
value={color}
onChangeComplete={(col) => setColor(col.hex)}
/>
</FormControl>
<FormControl>
<FormLabel htmlFor="stroke">
<Flex>
<Text flexGrow={1} fontWeight={'bold'}>
Stroke
</Text>
<Text>{strokeWidth}px</Text>
</Flex>
</FormLabel>
<Slider
value={strokeWidth}
onChange={setStroke}
min={0.5}
max={3}
step={0.5}
name={'stroke'}
>
<SliderTrack>
<SliderFilledTrack bg={color} />
</SliderTrack>
<SliderThumb />
</Slider>
</FormControl>
<FormControl>
<FormLabel htmlFor="size">
<Flex>
<Text flexGrow={1} fontWeight={'bold'}>
Size
</Text>
<Text>{size}px</Text>
</Flex>
</FormLabel>
<Slider value={size} onChange={setSize} min={12} max={64} step={1} name={'size'}>
<SliderTrack>
<SliderFilledTrack bg={color} />
</SliderTrack>
<SliderThumb />
</Slider>
</FormControl>
<FormControl>
<Button onClick={resetStyle}>Reset</Button>
</FormControl>
</Grid>
</DrawerBody>
</DrawerContent>
</Drawer>
</>
);
}
Example #22
Source File: TopNavigation.tsx From website with MIT License | 4 votes |
TopNavigation: React.FC<TopNavigationProps> = ({ title }) => {
const { isOpen, onOpen, onClose } = useDisclosure()
return (
<Grid
as="nav"
templateColumns={`1fr 1fr minmax(auto, ${theme.sizes['6xl']}) 1fr 1fr`}
backgroundColor="gray.900"
color="white"
height={['88px', '80px']}
zIndex={50}
>
<List display="flex" flexWrap="wrap" alignItems="center" gridColumn="3/4" m={0} p={0}>
<ListItem display="flex" alignItems="center" pos="relative" h="100%" mr="auto">
<NextChakraLink
href="/"
display="flex"
alignItems="center"
py=".5rem"
px="1rem"
h="100%"
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', textDecoration: 'none' }}
>
<Logo height={40} fill={theme.colors.white} title={title} />
</NextChakraLink>
</ListItem>
<ListItem display={['none', 'flex']} alignItems="center" h="100%">
<NextChakraLink
href="/community"
display="flex"
alignItems="center"
py="1.5rem"
px="1rem"
color="inherit"
h="100%"
lineHeight={1}
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', textDecoration: 'none' }}
>
Komunitas
</NextChakraLink>
</ListItem>
<ListItem display={['none', 'flex']} alignItems="center" h="100%">
<NextChakraLink
href="/submit-a-talk"
display="flex"
alignItems="center"
py="1.5rem"
px="1rem"
color="inherit"
h="100%"
lineHeight={1}
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', textDecoration: 'none' }}
>
Ajukan Topik
</NextChakraLink>
</ListItem>
<ListItem display={['none', 'flex']} alignItems="center" h="100%">
<NextChakraLink
href="/faq"
display="flex"
alignItems="center"
py="1.5rem"
px="1rem"
color="inherit"
h="100%"
lineHeight={1}
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', textDecoration: 'none' }}
>
FAQ
</NextChakraLink>
</ListItem>
<ListItem py="1.5rem" px="1rem" display={['flex', 'none']} alignItems="center" h="100%">
<IconButton variant="outline" aria-label="Open menu" icon={<HamburgerIcon />} onClick={onOpen} />
<Drawer isOpen={isOpen} placement="right" onClose={onClose}>
<DrawerOverlay />
<DrawerContent>
<DrawerCloseButton />
<DrawerHeader>Menu Utama</DrawerHeader>
<DrawerBody>
<NextChakraLink
href="/community"
display="flex"
alignItems="center"
py="1.5rem"
px="1rem"
color="inherit"
lineHeight={1}
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', textDecoration: 'none' }}
>
Komunitas
</NextChakraLink>
<NextChakraLink
href="/submit-a-talk"
display="flex"
alignItems="center"
py="1.5rem"
px="1rem"
color="inherit"
lineHeight={1}
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', textDecoration: 'none' }}
>
Ajukan Topik
</NextChakraLink>
<NextChakraLink
href="/faq"
display="flex"
alignItems="center"
py="1.5rem"
px="1rem"
color="inherit"
lineHeight={1}
_hover={{ bg: 'rgba(255, 255, 255, 0.1)', textDecoration: 'none' }}
>
FAQ
</NextChakraLink>
</DrawerBody>
</DrawerContent>
</Drawer>
</ListItem>
</List>
</Grid>
)
}
Example #23
Source File: create-database.tsx From ledokku with MIT License | 4 votes |
CreateDatabase = () => {
const location = useLocation();
const history = useHistory();
const toast = useToast();
const { data: dataDb } = useDatabaseQuery();
const [arrayOfCreateDbLogs, setArrayofCreateDbLogs] = useState<RealTimeLog[]>(
[]
);
const [isTerminalVisible, setIsTerminalVisible] = useState(false);
const [createDatabaseMutation] = useCreateDatabaseMutation();
const [
isDbCreationSuccess,
setIsDbCreationSuccess,
] = useState<DbCreationStatus>();
useCreateDatabaseLogsSubscription({
onSubscriptionData: (data) => {
const logsExist = data.subscriptionData.data?.createDatabaseLogs;
if (logsExist) {
setArrayofCreateDbLogs((currentLogs) => {
return [...currentLogs, logsExist];
});
if (logsExist.type === 'end:success') {
setIsDbCreationSuccess(DbCreationStatus.SUCCESS);
} else if (logsExist.type === 'end:failure') {
setIsDbCreationSuccess(DbCreationStatus.FAILURE);
}
}
},
});
const createDatabaseSchema = yup.object({
type: yup
.string()
.oneOf(['POSTGRESQL', 'MYSQL', 'MONGODB', 'REDIS'])
.required(),
name: yup.string().when('type', (type: DatabaseTypes) => {
return yup
.string()
.required('Database name is required')
.matches(/^[a-z0-9-]+$/)
.test(
'Name already exists',
`You already have created ${type} database with this name`,
(name) =>
!dataDb?.databases.find(
(db) => db.name === name && type === db.type
)
);
}),
});
const [
isDokkuPluginInstalled,
{ data, loading, error: isDokkuPluginInstalledError },
] = useIsPluginInstalledLazyQuery({
// we poll every 5 sec
pollInterval: 5000,
});
const formik = useFormik<{ name: string; type: DatabaseTypes }>({
initialValues: {
name: location.state ? (location.state as string) : '',
type: 'POSTGRESQL',
},
validateOnChange: true,
validationSchema: createDatabaseSchema,
onSubmit: async (values) => {
try {
await createDatabaseMutation({
variables: {
input: { name: values.name, type: values.type },
},
});
setIsTerminalVisible(true);
trackGoal(trackingGoals.createDatabase, 0);
} catch (error) {
toast.error(error.message);
}
},
});
const isPluginInstalled = data?.isPluginInstalled.isPluginInstalled;
const handleNext = () => {
setIsTerminalVisible(false);
const dbId = arrayOfCreateDbLogs[arrayOfCreateDbLogs.length - 1].message;
history.push(`database/${dbId}`);
};
// Effect for checking whether plugin is installed
useEffect(() => {
isDokkuPluginInstalled({
variables: {
pluginName: dbTypeToDokkuPlugin(formik.values.type),
},
});
}, [formik.values.type, isPluginInstalled, isDokkuPluginInstalled]);
// Effect for db creation
useEffect(() => {
isDbCreationSuccess === DbCreationStatus.FAILURE
? toast.error('Failed to create database')
: isDbCreationSuccess === DbCreationStatus.SUCCESS &&
toast.success('Database created successfully');
}, [isDbCreationSuccess, toast]);
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" my="4">
<Heading as="h2" size="md">
Create a new database
</Heading>
<Box mt="12">
{isTerminalVisible ? (
<>
<Text mb="2">
Creating <b>{formik.values.type}</b> database{' '}
<b>{formik.values.name}</b>
</Text>
<Text mb="2" color="gray.500">
Creating database usually takes a couple of minutes. Breathe in,
breathe out, logs are about to appear below:
</Text>
<Terminal>
{arrayOfCreateDbLogs.map((log) => (
<Text key={arrayOfCreateDbLogs.indexOf(log)} size="small">
{log.message}
</Text>
))}
</Terminal>
{!!isDbCreationSuccess &&
isDbCreationSuccess === DbCreationStatus.SUCCESS ? (
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
onClick={() => handleNext()}
rightIcon={<FiArrowRight size={20} />}
>
Next
</Button>
</Box>
) : !!isDbCreationSuccess &&
isDbCreationSuccess === DbCreationStatus.FAILURE ? (
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
onClick={() => {
setIsTerminalVisible(false);
formik.resetForm();
}}
rightIcon={<FiArrowLeft size={20} />}
>
Back
</Button>
</Box>
) : null}
</>
) : (
<Box mt="8">
<form onSubmit={formik.handleSubmit}>
<Box mt="12">
{loading && (
<Center>
<Spinner />
</Center>
)}
{isDokkuPluginInstalledError ? (
<Alert
status="error"
variant="top-accent"
flexDirection="column"
alignItems="flex-start"
borderBottomRadius="base"
boxShadow="md"
>
<AlertTitle mr={2}>Request failed</AlertTitle>
<AlertDescription>
{isDokkuPluginInstalledError.message}
</AlertDescription>
</Alert>
) : null}
{data?.isPluginInstalled.isPluginInstalled === false &&
!loading && (
<>
<Text mt="3">
Before creating a{' '}
<b>{formik.values.type.toLowerCase()}</b> database,
you will need to run this command on your dokku
server.
</Text>
<Terminal>{`sudo dokku plugin:install https://github.com/dokku/dokku-${dbTypeToDokkuPlugin(
formik.values.type
)}.git ${dbTypeToDokkuPlugin(
formik.values.type
)}`}</Terminal>
<Text mt="3">
Couple of seconds later you will be able to proceed
further.
</Text>
</>
)}
{data?.isPluginInstalled.isPluginInstalled === true &&
!loading && (
<SimpleGrid columns={{ sm: 1, md: 3 }}>
<FormControl
id="name"
isInvalid={Boolean(
formik.errors.name && formik.touched.name
)}
>
<FormLabel>Database name</FormLabel>
<Input
autoComplete="off"
id="name"
name="name"
value={formik.values.name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
/>
<FormErrorMessage>
{formik.errors.name}
</FormErrorMessage>
</FormControl>
</SimpleGrid>
)}
</Box>
<Box mt="12">
<Text mb="2">Choose your database</Text>
<Grid
templateColumns={{
base: 'repeat(2, minmax(0, 1fr))',
md: 'repeat(4, minmax(0, 1fr))',
}}
gap="4"
>
<DatabaseBox
selected={formik.values.type === 'POSTGRESQL'}
label="PostgreSQL"
icon={<PostgreSQLIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'POSTGRESQL')}
/>
<DatabaseBox
selected={formik.values.type === 'MYSQL'}
label="MySQL"
icon={<MySQLIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'MYSQL')}
/>
<DatabaseBox
selected={formik.values.type === 'MONGODB'}
label="Mongo"
icon={<MongoIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'MONGODB')}
/>
<DatabaseBox
selected={formik.values.type === 'REDIS'}
label="Redis"
icon={<RedisIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'REDIS')}
/>
</Grid>
</Box>
<Box mt="12" display="flex" justifyContent="flex-end">
<Button
isLoading={formik.isSubmitting}
disabled={
data?.isPluginInstalled.isPluginInstalled === false ||
!formik.values.name ||
!!formik.errors.name ||
!dataDb?.databases
}
rightIcon={<FiArrowRight size={20} />}
type="submit"
>
Create
</Button>
</Box>
</form>
</Box>
)}
</Box>
</Container>
</>
);
}
Example #24
Source File: create-app.tsx From ledokku with MIT License | 4 votes |
CreateApp = () => {
const history = useHistory();
const toast = useToast();
const createAppSchema = yup.object({
type: yup
.string()
.oneOf(['GITHUB', 'GITLAB', 'DOCKER', 'DOKKU'])
.required(),
});
const formik = useFormik<{ type: AppTypes }>({
initialValues: {
type: AppTypes.GITHUB,
},
validateOnChange: true,
validationSchema: createAppSchema,
onSubmit: async (values) => {
try {
values.type === AppTypes.GITHUB
? history.push('/create-app-github')
: history.push('/create-app-dokku');
} catch (error) {
toast.error(error.message);
}
},
});
return (
<>
<HeaderContainer>
<Header />
</HeaderContainer>
<Container maxW="5xl" my="4">
<Heading py="2" as="h2" size="md">
App source
</Heading>
<Box mt="24">
<Box mt="4">
<form onSubmit={formik.handleSubmit}>
<Box mt="20">
<Text mb="5" color="gray.500">
Choose between creating app from a Github repository or
creating a standalone Dokku app.
</Text>
<Grid
templateColumns={{
base: 'repeat(2, minmax(0, 1fr))',
md: 'repeat(4, minmax(0, 1fr))',
}}
gap="4"
>
<SourceBox
selected={formik.values.type === AppTypes.GITHUB}
label="GitHub"
icon={<GithubIcon size={40} />}
onClick={() => formik.setFieldValue('type', 'GITHUB')}
/>
<SourceBox
selected={formik.values.type === AppTypes.DOKKU}
label="Dokku"
icon={
<Image
boxSize="48px"
objectFit="cover"
src="/dokku.png"
alt="dokkuLogo"
/>
}
onClick={() => formik.setFieldValue('type', 'DOKKU')}
/>
<SourceBox
selected={formik.values.type === AppTypes.GITLAB}
label="Gitlab"
icon={<GitlabIcon size={40} />}
badge={
<Badge ml="1" colorScheme="red">
Coming soon
</Badge>
}
// Uncomment this when we can handle docker deployments
// onClick={() => formik.setFieldValue('type', 'GITLAB')}
/>
<SourceBox
selected={formik.values.type === AppTypes.DOCKER}
label="Docker"
icon={<DockerIcon size={40} />}
badge={
<Badge ml="1" colorScheme="red">
Coming soon
</Badge>
}
// Uncomment this when we can handle docker deployments
// onClick={() => formik.setFieldValue('type', 'DOCKER')}
/>
</Grid>
</Box>
<Box mt="36" display="flex" justifyContent="flex-end">
<Button
isLoading={formik.isSubmitting}
disabled={!formik.values.type || !!formik.errors.type}
rightIcon={<FiArrowRight size={20} />}
type="submit"
>
Next
</Button>
</Box>
</form>
</Box>
</Box>
</Container>
</>
);
}
Example #25
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 #26
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 #27
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 #28
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 #29
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>
</>
);
}