diff --git a/packages/frontend/public/wave-border.svg b/packages/frontend/public/wave-border.svg
deleted file mode 100644
index 94137bb5..00000000
--- a/packages/frontend/public/wave-border.svg
+++ /dev/null
@@ -1,10 +0,0 @@
-
diff --git a/packages/frontend/public/wavy-border-fill.svg b/packages/frontend/public/wavy-border-fill.svg
new file mode 100644
index 00000000..4486705b
--- /dev/null
+++ b/packages/frontend/public/wavy-border-fill.svg
@@ -0,0 +1,4 @@
+
diff --git a/packages/frontend/public/wavy-border-line-and-fill.svg b/packages/frontend/public/wavy-border-line-and-fill.svg
new file mode 100644
index 00000000..27df94a3
--- /dev/null
+++ b/packages/frontend/public/wavy-border-line-and-fill.svg
@@ -0,0 +1,15 @@
+
diff --git a/packages/frontend/public/wavy-border-line.svg b/packages/frontend/public/wavy-border-line.svg
new file mode 100644
index 00000000..eba75bd0
--- /dev/null
+++ b/packages/frontend/public/wavy-border-line.svg
@@ -0,0 +1,10 @@
+
diff --git a/packages/frontend/src/App.tsx b/packages/frontend/src/App.tsx
index a32fcd0c..0e171b94 100644
--- a/packages/frontend/src/App.tsx
+++ b/packages/frontend/src/App.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
-import OrgSlug from './pages/OrgSlug';
import Projects from './pages/org-slug';
import Settings from './pages/org-slug/Settings';
import {
@@ -11,11 +10,12 @@ import {
import ProjectSearchLayout from './layouts/ProjectSearch';
import Index from './pages';
import Login from './pages/Login';
+import { DashboardLayout } from 'pages/org-slug/layout';
const router = createBrowserRouter([
{
path: ':orgSlug',
- element: ,
+ element: ,
children: [
{
element: ,
diff --git a/packages/frontend/src/assets/templates.ts b/packages/frontend/src/assets/templates.ts
index 2efca8f8..84c60bb5 100644
--- a/packages/frontend/src/assets/templates.ts
+++ b/packages/frontend/src/assets/templates.ts
@@ -2,31 +2,36 @@ export default [
{
id: '1',
name: 'Progressive Web App (PWA)',
- icon: '^',
+ icon: 'pwa',
repoFullName: `${process.env.REACT_APP_GITHUB_PWA_TEMPLATE_REPO}`,
+ isComingSoon: false,
},
{
id: '2',
name: 'Image Upload PWA',
- icon: '^',
+ icon: 'pwa',
repoFullName: `${process.env.REACT_APP_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
+ isComingSoon: false,
},
{
id: '3',
name: 'Kotlin',
- icon: '^',
+ icon: 'kotlin',
repoFullName: '',
+ isComingSoon: false,
},
{
id: '4',
name: 'React Native',
- icon: '^',
+ icon: 'react-native',
repoFullName: '',
+ isComingSoon: false,
},
{
id: '5',
name: 'Swift',
- icon: '^',
+ icon: 'swift',
repoFullName: '',
+ isComingSoon: true,
},
];
diff --git a/packages/frontend/src/components/projects/ProjectCard/ProjectCard.tsx b/packages/frontend/src/components/projects/ProjectCard/ProjectCard.tsx
index 85603037..5c7efb15 100644
--- a/packages/frontend/src/components/projects/ProjectCard/ProjectCard.tsx
+++ b/packages/frontend/src/components/projects/ProjectCard/ProjectCard.tsx
@@ -87,7 +87,10 @@ export const ProjectCard = ({
{/* Wave */}
-
+
{/* Lower content */}
{/* Latest deployment */}
diff --git a/packages/frontend/src/components/projects/create/TemplateCard.tsx b/packages/frontend/src/components/projects/create/TemplateCard.tsx
deleted file mode 100644
index 3625b64b..00000000
--- a/packages/frontend/src/components/projects/create/TemplateCard.tsx
+++ /dev/null
@@ -1,53 +0,0 @@
-import React from 'react';
-import toast from 'react-hot-toast';
-
-import { IconButton, Typography } from '@material-tailwind/react';
-
-import { Link } from 'react-router-dom';
-
-interface TemplateDetails {
- id: string;
- name: string;
- icon: string;
-}
-interface TemplateCardProps {
- template: TemplateDetails;
- isGitAuth: boolean;
-}
-
-const CardDetails = ({ template }: { template: TemplateDetails }) => {
- return (
-
-
- {template.icon} {template.name}
-
-
-
- {'>'}
-
-
-
- );
-};
-
-const TemplateCard: React.FC
= ({ template, isGitAuth }) => {
- return isGitAuth ? (
-
-
-
- ) : (
-
- toast.error('Connect Git account to start with a template')
- }
- >
-
-
- );
-};
-
-export default TemplateCard;
diff --git a/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx
new file mode 100644
index 00000000..b7c549fd
--- /dev/null
+++ b/packages/frontend/src/components/projects/create/TemplateCard/TemplateCard.tsx
@@ -0,0 +1,85 @@
+import { Button } from 'components/shared/Button';
+import {
+ ArrowRightCircleIcon,
+ ClockOutlineIcon,
+ TemplateIcon,
+ TemplateIconType,
+} from 'components/shared/CustomIcon';
+import { Tag } from 'components/shared/Tag';
+import React, { ComponentPropsWithoutRef, useCallback } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { useToast } from 'components/shared/Toast';
+import { cn } from 'utils/classnames';
+
+export interface TemplateDetail {
+ id: string;
+ name: string;
+ icon: string;
+ repoFullName?: string;
+ isComingSoon?: boolean;
+}
+
+export interface TemplateCardProps extends ComponentPropsWithoutRef<'div'> {
+ template: TemplateDetail;
+ isGitAuth: boolean;
+}
+
+export const TemplateCard = ({ template, isGitAuth }: TemplateCardProps) => {
+ const { toast, dismiss } = useToast();
+ const navigate = useNavigate();
+
+ const handleClick = useCallback(() => {
+ if (template?.isComingSoon) {
+ return toast({
+ id: 'coming-soon',
+ title: 'This template is coming soon',
+ variant: 'info',
+ onDismiss: dismiss,
+ });
+ }
+ if (isGitAuth) {
+ return navigate(`/template?templateId=${template.id}`);
+ }
+ return toast({
+ id: 'connect-git-account',
+ title: 'Connect Git account to start with a template',
+ variant: 'error',
+ onDismiss: dismiss,
+ });
+ }, [isGitAuth, navigate, template, toast]);
+
+ return (
+
+ );
+};
diff --git a/packages/frontend/src/components/projects/create/TemplateCard/index.ts b/packages/frontend/src/components/projects/create/TemplateCard/index.ts
new file mode 100644
index 00000000..55250834
--- /dev/null
+++ b/packages/frontend/src/components/projects/create/TemplateCard/index.ts
@@ -0,0 +1 @@
+export * from './TemplateCard';
diff --git a/packages/frontend/src/components/shared/Button/Button.theme.ts b/packages/frontend/src/components/shared/Button/Button.theme.ts
index 5d5ba72e..262cb8f9 100644
--- a/packages/frontend/src/components/shared/Button/Button.theme.ts
+++ b/packages/frontend/src/components/shared/Button/Button.theme.ts
@@ -150,7 +150,7 @@ export const buttonTheme = tv(
{
size: 'md',
iconOnly: true,
- class: ['py-3.25', 'px-3.25'],
+ class: ['py-3', 'px-3'],
},
{
size: 'sm',
diff --git a/packages/frontend/src/components/shared/CustomIcon/ArrowRightCircleIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/ArrowRightCircleIcon.tsx
new file mode 100644
index 00000000..cce7cc58
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/ArrowRightCircleIcon.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from './CustomIcon';
+
+export const ArrowRightCircleIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/ClockOutlineIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/ClockOutlineIcon.tsx
new file mode 100644
index 00000000..01fec9ba
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/ClockOutlineIcon.tsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from './CustomIcon';
+
+export const ClockOutlineIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/index.ts b/packages/frontend/src/components/shared/CustomIcon/index.ts
index 72205c83..754404be 100644
--- a/packages/frontend/src/components/shared/CustomIcon/index.ts
+++ b/packages/frontend/src/components/shared/CustomIcon/index.ts
@@ -33,4 +33,9 @@ export * from './GitHubLogo';
export * from './ClockIcon';
export * from './HorizontalDotIcon';
export * from './WarningDiamondIcon';
+export * from './ArrowRightCircleIcon';
+export * from './ClockOutlineIcon';
export * from './ArrowRightCircleFilledIcon';
+
+// Templates
+export * from './templates';
diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/KotlinIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/templates/KotlinIcon.tsx
new file mode 100644
index 00000000..973b5f4f
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/templates/KotlinIcon.tsx
@@ -0,0 +1,130 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from '../CustomIcon';
+
+export const KotlinIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/PWAIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/templates/PWAIcon.tsx
new file mode 100644
index 00000000..f0edb351
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/templates/PWAIcon.tsx
@@ -0,0 +1,88 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from '../CustomIcon';
+
+export const PWAIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/ReactNativeIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/templates/ReactNativeIcon.tsx
new file mode 100644
index 00000000..bc170f98
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/templates/ReactNativeIcon.tsx
@@ -0,0 +1,98 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from '../CustomIcon';
+
+export const ReactNativeIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/SwiftIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/templates/SwiftIcon.tsx
new file mode 100644
index 00000000..d3422dff
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/templates/SwiftIcon.tsx
@@ -0,0 +1,102 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from '../CustomIcon';
+
+export const SwitfIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/TemplateIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/templates/TemplateIcon.tsx
new file mode 100644
index 00000000..c7b307bb
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/templates/TemplateIcon.tsx
@@ -0,0 +1,42 @@
+import React, { useMemo } from 'react';
+import { CustomIconProps } from '../CustomIcon';
+import { ReactNativeIcon } from './ReactNativeIcon';
+import { cloneIcon } from 'utils/cloneIcon';
+import { PWAIcon } from './PWAIcon';
+import { WebAppIcon } from './WebAppIcon';
+import { KotlinIcon } from './KotlinIcon';
+import { SwitfIcon } from './SwiftIcon';
+
+const TEMPLATE_ICONS = [
+ 'react-native',
+ 'pwa',
+ 'web',
+ 'kotlin',
+ 'swift',
+] as const;
+export type TemplateIconType = (typeof TEMPLATE_ICONS)[number];
+
+export interface TemplateIconProps extends CustomIconProps {
+ type: TemplateIconType;
+}
+
+export const TemplateIcon = ({ type, ...props }: TemplateIconProps) => {
+ const renderIcon = useMemo(() => {
+ switch (type) {
+ case 'react-native':
+ return ;
+ case 'pwa':
+ return ;
+ case 'web':
+ return ;
+ case 'kotlin':
+ return ;
+ case 'swift':
+ return ;
+ default:
+ throw new Error(`Invalid template icon type: ${type}`);
+ }
+ }, [type]);
+
+ return cloneIcon(renderIcon, props) as JSX.Element;
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/WebAppIcon.tsx b/packages/frontend/src/components/shared/CustomIcon/templates/WebAppIcon.tsx
new file mode 100644
index 00000000..f707fccc
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/templates/WebAppIcon.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import { CustomIcon, CustomIconProps } from '../CustomIcon';
+
+export const WebAppIcon = (props: CustomIconProps) => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/frontend/src/components/shared/CustomIcon/templates/index.ts b/packages/frontend/src/components/shared/CustomIcon/templates/index.ts
new file mode 100644
index 00000000..e2879355
--- /dev/null
+++ b/packages/frontend/src/components/shared/CustomIcon/templates/index.ts
@@ -0,0 +1 @@
+export * from './TemplateIcon';
diff --git a/packages/frontend/src/components/shared/Sidebar/Sidebar.tsx b/packages/frontend/src/components/shared/Sidebar/Sidebar.tsx
index 0d746749..c60ab4d3 100644
--- a/packages/frontend/src/components/shared/Sidebar/Sidebar.tsx
+++ b/packages/frontend/src/components/shared/Sidebar/Sidebar.tsx
@@ -43,7 +43,7 @@ export const Sidebar = () => {
}, [disconnect, navigate]);
return (
-
+
+
);
};
diff --git a/packages/frontend/src/components/shared/WavyBorder/WavyBorder.tsx b/packages/frontend/src/components/shared/WavyBorder/WavyBorder.tsx
index 45bc7234..805272f1 100644
--- a/packages/frontend/src/components/shared/WavyBorder/WavyBorder.tsx
+++ b/packages/frontend/src/components/shared/WavyBorder/WavyBorder.tsx
@@ -1,16 +1,44 @@
-import React, { ComponentPropsWithoutRef } from 'react';
+import React, { ComponentPropsWithoutRef, useMemo } from 'react';
import { cn } from 'utils/classnames';
-export interface WavyBorderProps extends ComponentPropsWithoutRef<'div'> {}
+type WaveBorderVariant = 'stroke' | 'stroke-and-fill';
+
+export interface WavyBorderProps extends ComponentPropsWithoutRef<'div'> {
+ variant?: WaveBorderVariant;
+}
export const WavyBorder = ({
className,
- children,
+ variant = 'stroke',
...props
}: WavyBorderProps) => {
+ const imageSrc = useMemo(() => {
+ switch (variant) {
+ case 'stroke-and-fill':
+ return '/wavy-border-line-and-fill.svg';
+ case 'stroke':
+ default:
+ return '/wavy-border-line.svg';
+ }
+ }, [variant]);
+
return (
-
- {children}
+
);
};
diff --git a/packages/frontend/src/index.css b/packages/frontend/src/index.css
index 60291de7..502904a5 100644
--- a/packages/frontend/src/index.css
+++ b/packages/frontend/src/index.css
@@ -153,12 +153,4 @@
.focus-ring {
@apply focus-visible:ring-[3px] focus-visible:ring-snowball-200 focus-visible:ring-offset-1 focus-visible:ring-offset-snowball-500 focus-visible:outline-none;
}
-
- .wave {
- background-image: url('../public/wave-border.svg');
- background-repeat: repeat-x;
- background-position: top;
- background-size: cover;
- @apply w-full h-1;
- }
}
diff --git a/packages/frontend/src/pages/OrgSlug.tsx b/packages/frontend/src/pages/OrgSlug.tsx
deleted file mode 100644
index 834bb15d..00000000
--- a/packages/frontend/src/pages/OrgSlug.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import { Outlet } from 'react-router-dom';
-
-import { OctokitProvider } from 'context/OctokitContext';
-import { Sidebar } from 'components/shared/Sidebar';
-
-const OrgSlug = () => {
- return (
-
- );
-};
-
-export default OrgSlug;
diff --git a/packages/frontend/src/pages/org-slug/layout.tsx b/packages/frontend/src/pages/org-slug/layout.tsx
new file mode 100644
index 00000000..c1be6b8c
--- /dev/null
+++ b/packages/frontend/src/pages/org-slug/layout.tsx
@@ -0,0 +1,31 @@
+import { Sidebar } from 'components/shared/Sidebar';
+import { OctokitProvider } from 'context/OctokitContext';
+import React, { ComponentPropsWithoutRef } from 'react';
+import { Outlet } from 'react-router-dom';
+import { cn } from 'utils/classnames';
+
+export interface DashboardLayoutProps
+ extends ComponentPropsWithoutRef<'section'> {}
+
+export const DashboardLayout = ({
+ className,
+ children,
+ ...props
+}: DashboardLayoutProps) => {
+ return (
+
+ );
+};
diff --git a/packages/frontend/src/pages/org-slug/projects/Create.tsx b/packages/frontend/src/pages/org-slug/projects/Create.tsx
deleted file mode 100644
index 2f12b981..00000000
--- a/packages/frontend/src/pages/org-slug/projects/Create.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import { Outlet, Link, useParams } from 'react-router-dom';
-
-import { IconButton } from '@material-tailwind/react';
-
-import HorizontalLine from '../../../components/HorizontalLine';
-
-const CreateProject = () => {
- const { orgSlug } = useParams();
- return (
-
-
-
-
Create new project
-
-
-
-
- X
-
-
-
-
-
-
-
- );
-};
-
-export default CreateProject;
diff --git a/packages/frontend/src/pages/org-slug/projects/create/index.tsx b/packages/frontend/src/pages/org-slug/projects/create/index.tsx
index 6d2598f4..48c576a3 100644
--- a/packages/frontend/src/pages/org-slug/projects/create/index.tsx
+++ b/packages/frontend/src/pages/org-slug/projects/create/index.tsx
@@ -1,29 +1,36 @@
import React from 'react';
-import templates from '../../../../assets/templates';
-import TemplateCard from '../../../../components/projects/create/TemplateCard';
-import RepositoryList from '../../../../components/projects/create/RepositoryList';
-import ConnectAccount from '../../../../components/projects/create/ConnectAccount';
-import { useOctokit } from '../../../../context/OctokitContext';
+import templates from 'assets/templates';
+import { RepositoryList } from 'components/projects/create/RepositoryList';
+import ConnectAccount from 'components/projects/create/ConnectAccount';
+import { useOctokit } from 'context/OctokitContext';
+import { Heading } from 'components/shared/Heading';
+import { TemplateCard } from 'components/projects/create/TemplateCard';
const NewProject = () => {
const { octokit, updateAuth, isAuth } = useOctokit();
return isAuth ? (
<>
-
Start with template
-
- {templates.map((template) => {
- return (
-
- );
- })}
+
+
+ Start with template
+
+
+ {templates.map((template) => {
+ return (
+
+ );
+ })}
+
-
Import a repository
+
+ Import a repository
+
>
) : (
diff --git a/packages/frontend/src/pages/org-slug/projects/create/layout.tsx b/packages/frontend/src/pages/org-slug/projects/create/layout.tsx
new file mode 100644
index 00000000..3dad337a
--- /dev/null
+++ b/packages/frontend/src/pages/org-slug/projects/create/layout.tsx
@@ -0,0 +1,39 @@
+import React, { ComponentPropsWithoutRef } from 'react';
+import { Link, Outlet, useParams } from 'react-router-dom';
+
+import { Heading } from 'components/shared/Heading';
+import { WavyBorder } from 'components/shared/WavyBorder';
+import { Button } from 'components/shared/Button';
+import { CrossIcon } from 'components/shared/CustomIcon';
+import { cn } from 'utils/classnames';
+
+export interface CreateProjectLayoutProps
+ extends ComponentPropsWithoutRef<'section'> {}
+
+export const CreateProjectLayout = ({
+ className,
+ ...props
+}: CreateProjectLayoutProps) => {
+ const { orgSlug } = useParams();
+
+ return (
+
+
+
+
+ Create new project
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/packages/frontend/src/pages/org-slug/projects/routes.tsx b/packages/frontend/src/pages/org-slug/projects/routes.tsx
index 993e22b3..e4dd5756 100644
--- a/packages/frontend/src/pages/org-slug/projects/routes.tsx
+++ b/packages/frontend/src/pages/org-slug/projects/routes.tsx
@@ -1,16 +1,16 @@
import React from 'react';
-import CreateProject from './Create';
import Id from './Id';
import AddDomain from './id/settings/domains/add';
import { createProjectRoutes } from './create/routes';
import { projectTabRoutes } from './id/routes';
import { addDomainRoutes } from './id/settings/domains/add/routes';
+import { CreateProjectLayout } from './create/layout';
export const projectsRoutesWithoutSearch = [
{
path: 'create',
- element:
,
+ element:
,
children: createProjectRoutes,
},
{
diff --git a/packages/frontend/tailwind.config.js b/packages/frontend/tailwind.config.js
index eb0bb315..c362e898 100644
--- a/packages/frontend/tailwind.config.js
+++ b/packages/frontend/tailwind.config.js
@@ -158,6 +158,7 @@ export default withMT({
field: '0px 1px 2px rgba(0, 0, 0, 0.04)',
inset: 'inset 0px 1px 0px rgba(8, 47, 86, 0.06)',
card: '0px 0px 0px 1px #E8F0F7, 0px 2px 4px rgba(8, 47, 86, 0.04)',
+ 'card-sm': '0px 1px 2px -1px rgba(4, 25, 47, 0.08)',
},
spacing: {
2.5: '0.625rem',