Switch from cra to vite

This commit is contained in:
Gilbert
2024-04-11 21:48:58 -05:00
parent cc8f9527da
commit f8d706233e
36 changed files with 5163 additions and 6582 deletions
+16 -16
View File
@@ -1,51 +1,51 @@
import React from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import React from "react";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Projects from './pages/org-slug';
import Settings from './pages/org-slug/Settings';
import Projects from "./pages/org-slug";
import Settings from "./pages/org-slug/Settings";
import {
projectsRoutesWithSearch,
projectsRoutesWithoutSearch,
} from './pages/org-slug/projects/routes';
import ProjectSearchLayout from './layouts/ProjectSearch';
import Index from './pages';
import Login from './pages/Login';
import { DashboardLayout } from 'pages/org-slug/layout';
} from "./pages/org-slug/projects/routes";
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',
path: ":orgSlug",
element: <DashboardLayout />,
children: [
{
element: <ProjectSearchLayout />,
children: [
{
path: '',
path: "",
element: <Projects />,
},
{
path: 'projects',
path: "projects",
children: projectsRoutesWithSearch,
},
],
},
{
path: 'settings',
path: "settings",
element: <Settings />,
},
{
path: 'projects',
path: "projects",
children: projectsRoutesWithoutSearch,
},
],
},
{
path: '/',
path: "/",
element: <Index />,
},
{
path: '/login',
path: "/login",
element: <Login />,
},
]);
+20 -20
View File
@@ -1,37 +1,37 @@
export default [
{
id: '1',
name: 'Progressive Web App (PWA)',
icon: 'pwa',
repoFullName: `${process.env.REACT_APP_GITHUB_PWA_TEMPLATE_REPO}`,
id: "1",
name: "Progressive Web App (PWA)",
icon: "pwa",
repoFullName: `${import.meta.env.VITE_GITHUB_PWA_TEMPLATE_REPO}`,
isComingSoon: false,
},
{
id: '2',
name: 'Image Upload PWA',
icon: 'pwa',
repoFullName: `${process.env.REACT_APP_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
id: "2",
name: "Image Upload PWA",
icon: "pwa",
repoFullName: `${import.meta.env.VITE_GITHUB_IMAGE_UPLOAD_PWA_TEMPLATE_REPO}`,
isComingSoon: false,
},
{
id: '3',
name: 'Kotlin',
icon: 'kotlin',
repoFullName: '',
id: "3",
name: "Kotlin",
icon: "kotlin",
repoFullName: "",
isComingSoon: true,
},
{
id: '4',
name: 'React Native',
icon: 'react-native',
repoFullName: '',
id: "4",
name: "React Native",
icon: "react-native",
repoFullName: "",
isComingSoon: true,
},
{
id: '5',
name: 'Swift',
icon: 'swift',
repoFullName: '',
id: "5",
name: "Swift",
icon: "swift",
repoFullName: "",
isComingSoon: true,
},
];
+7 -7
View File
@@ -1,9 +1,9 @@
import React from 'react';
import { StepperNav } from 'vertical-stepper-nav';
import React from "react";
import { StepperNav } from "./VerticalStepper";
const COLOR_COMPLETED = '#059669';
const COLOR_ACTIVE = '#CFE6FC';
const COLOR_NOT_STARTED = '#F1F5F9';
const COLOR_COMPLETED = "#059669";
const COLOR_ACTIVE = "#CFE6FC";
const COLOR_NOT_STARTED = "#F1F5F9";
interface StepperValue {
step: number;
@@ -25,8 +25,8 @@ const Stepper = ({ activeStep, stepperValues }: StepperProps) => {
<div
className={`text-sm ${
activeStep === stepperValue.step
? 'text-black font-semibold'
: 'text-gray-600'
? "text-black font-semibold"
: "text-gray-600"
}`}
>
{stepperValue.label}
@@ -0,0 +1,121 @@
import React from 'react';
import * as CSS from 'csstype';
//
// Nav
//
export interface IStepDescription {
stepContent: () => JSX.Element;
stepStateColor?: string;
stepStatusCircleSize?: number;
onClickHandler?: () => void | undefined;
}
export interface IStepperNavProps {
steps: IStepDescription[];
}
export const StepperNav = (props: IStepperNavProps): JSX.Element => {
return (
<nav>
{props.steps.map(
(
{ stepContent, stepStateColor, onClickHandler, stepStatusCircleSize },
index,
) => (
<div key={index}>
<Step
stepContent={stepContent}
statusColor={stepStateColor}
onClickHandler={onClickHandler}
statusCircleSize={stepStatusCircleSize}
/>
{index !== props.steps.length - 1 && (
<div
style={{
paddingLeft: `${(stepStatusCircleSize ?? 16) / 2 + 1}px`,
}}
>
<Separator />
</div>
)}
</div>
),
)}
</nav>
);
};
//
// Separator
//
const separatorStyles = {
height: '5vh',
width: 2,
border: '1px solid #E1E1E1',
background: '#E1E1E1',
};
export interface ISeparator {
height?: string | number;
}
export const Separator = ({ height }: ISeparator): JSX.Element => {
return <div style={{ ...separatorStyles, height: height ?? '5vh' }} />;
};
//
// Step
//
export interface IStep {
stepContent: () => JSX.Element;
statusColor?: string;
statusCircleSize?: number;
onClickHandler?: (
event?: React.MouseEvent<HTMLDivElement>,
) => void | undefined;
}
const buttonContainerStyles: CSS.Properties = {
display: 'inline-flex',
flexWrap: 'wrap',
gap: '12px',
padding: '2px',
cursor: 'pointer',
};
export const Step = ({
stepContent,
statusColor,
statusCircleSize,
onClickHandler,
}: IStep): JSX.Element => {
const circleStyles = {
borderRadius: statusCircleSize ?? 16,
width: statusCircleSize ?? 16,
height: statusCircleSize ?? 16,
border: '2px solid #E1E1E1',
background: statusColor ?? 'white',
};
const keyDownHandler = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.keyCode === 13 || event.keyCode === 32) {
onClickHandler?.();
}
};
return (
<div
tabIndex={0}
onClick={onClickHandler}
onKeyDown={keyDownHandler}
role="button"
style={{ ...buttonContainerStyles }}
>
<div>
<div style={circleStyles} />
</div>
<div style={{ paddingBottom: 2 }}>{stepContent()}</div>
</div>
);
};
@@ -1,23 +1,23 @@
import React from 'react';
import OauthPopup from 'react-oauth-popup';
import React from "react";
import OauthPopup from "react-oauth-popup";
import { useGQLClient } from '../../../context/GQLClientContext';
import { Button } from 'components/shared/Button';
import { useGQLClient } from "../../../context/GQLClientContext";
import { Button } from "../../shared/Button";
import {
GitIcon,
EllipsesIcon,
SnowballIcon,
GithubIcon,
GitTeaIcon,
} from 'components/shared/CustomIcon';
import { useToast } from 'components/shared/Toast';
import { IconWithFrame } from 'components/shared/IconWithFrame';
import { Heading } from 'components/shared/Heading';
import { MockConnectGitCard } from './MockConnectGitCard';
} from "../../shared/CustomIcon";
import { useToast } from "../../shared/Toast";
import { IconWithFrame } from "../../shared/IconWithFrame";
import { Heading } from "../../shared/Heading";
import { MockConnectGitCard } from "./MockConnectGitCard";
const SCOPES = 'repo user';
const SCOPES = "repo user";
const GITHUB_OAUTH_URL = `https://github.com/login/oauth/authorize?client_id=${
process.env.REACT_APP_GITHUB_CLIENT_ID
import.meta.env.VITE_GITHUB_CLIENT_ID
}&scope=${encodeURIComponent(SCOPES)}`;
interface ConnectAccountInterface {
@@ -39,9 +39,9 @@ const ConnectAccount: React.FC<ConnectAccountInterface> = ({
onToken(token);
toast({
onDismiss: dismiss,
id: 'connected-to-github',
title: 'The Git account is connected.',
variant: 'success',
id: "connected-to-github",
title: "The Git account is connected.",
variant: "success",
});
};
@@ -1,6 +1,6 @@
import React from 'react';
import React from "react";
import { Tabs } from 'components/shared/Tabs';
import { Tabs } from "components/shared/Tabs";
const ConnectAccountTabPanel: React.FC = () => {
return (
@@ -11,8 +11,8 @@ const ConnectAccountTabPanel: React.FC = () => {
>
<Tabs.List>
{[
{ title: 'Import a repository' },
{ title: 'Start with a template' },
{ title: "Import a repository" },
{ title: "Start with a template" },
].map(({ title }, index) => (
<Tabs.Trigger value={title} key={index}>
{title}
@@ -1,17 +1,17 @@
import React, { useCallback, useEffect } from 'react';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import React, { useCallback, useEffect } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { DeployStep, DeployStatus } from './DeployStep';
import { Stopwatch, setStopWatchOffset } from 'components/StopWatch';
import { Heading } from 'components/shared/Heading';
import { Button } from 'components/shared/Button';
import { ClockOutlineIcon, WarningIcon } from 'components/shared/CustomIcon';
import { CancelDeploymentDialog } from 'components/projects/Dialog/CancelDeploymentDialog';
import { DeployStep, DeployStatus } from "./DeployStep";
import { Stopwatch, setStopWatchOffset } from "../../StopWatch";
import { Heading } from "../../shared/Heading";
import { Button } from "../../shared/Button";
import { ClockOutlineIcon, WarningIcon } from "../../shared/CustomIcon";
import { CancelDeploymentDialog } from "../../projects/Dialog/CancelDeploymentDialog";
const TIMEOUT_DURATION = 5000;
const Deploy = () => {
const [searchParams] = useSearchParams();
const projectId = searchParams.get('projectId');
const projectId = searchParams.get("projectId");
const [open, setOpen] = React.useState(false);
const handleOpen = () => setOpen(!open);
@@ -1,11 +1,11 @@
import React, { useState } from 'react';
import React, { useState } from "react";
import { Collapse } from '@material-tailwind/react';
import { Collapse } from "@material-tailwind/react";
import { Stopwatch, setStopWatchOffset } from '../../StopWatch';
import FormatMillisecond from '../../FormatMilliSecond';
import processLogs from '../../../assets/process-logs.json';
import { cn } from 'utils/classnames';
import { Stopwatch, setStopWatchOffset } from "../../StopWatch";
import FormatMillisecond from "../../FormatMilliSecond";
import processLogs from "../../../assets/process-logs.json";
import { cn } from "utils/classnames";
import {
CheckRoundFilledIcon,
ClockOutlineIcon,
@@ -13,15 +13,15 @@ import {
LoaderIcon,
MinusCircleIcon,
PlusIcon,
} from 'components/shared/CustomIcon';
import { Button } from 'components/shared/Button';
import { useToast } from 'components/shared/Toast';
import { useIntersectionObserver } from 'usehooks-ts';
} from "components/shared/CustomIcon";
import { Button } from "components/shared/Button";
import { useToast } from "components/shared/Toast";
import { useIntersectionObserver } from "usehooks-ts";
enum DeployStatus {
PROCESSING = 'progress',
COMPLETE = 'complete',
NOT_STARTED = 'notStarted',
PROCESSING = "progress",
COMPLETE = "complete",
NOT_STARTED = "notStarted",
}
interface DeployStepsProps {
@@ -52,8 +52,8 @@ const DeployStep = ({
{/* Collapisble trigger */}
<button
className={cn(
'flex justify-between w-full py-5 gap-2',
disableCollapse && 'cursor-auto',
"flex justify-between w-full py-5 gap-2",
disableCollapse && "cursor-auto",
)}
tabIndex={disableCollapse ? -1 : undefined}
onClick={() => {
@@ -62,7 +62,7 @@ const DeployStep = ({
}
}}
>
<div className={cn('grow flex items-center gap-3')}>
<div className={cn("grow flex items-center gap-3")}>
{/* Icon */}
<div className="w-6 h-6 grid place-content-center">
{status === DeployStatus.NOT_STARTED && (
@@ -84,8 +84,8 @@ const DeployStep = ({
{/* Title */}
<span
className={cn(
'text-left text-sm md:text-base',
status === DeployStatus.PROCESSING && 'text-elements-link',
"text-left text-sm md:text-base",
status === DeployStatus.PROCESSING && "text-elements-link",
)}
>
{title}
@@ -107,7 +107,7 @@ const DeployStep = ({
size={15}
/>
</div>
<FormatMillisecond time={Number(processTime)} />{' '}
<FormatMillisecond time={Number(processTime)} />{" "}
</div>
)}
</button>
@@ -133,15 +133,15 @@ const DeployStep = ({
)}
{/* Copy log button */}
<div className={cn('sticky bottom-4 left-1/2 flex justify-center')}>
<div className={cn("sticky bottom-4 left-1/2 flex justify-center")}>
<Button
size="xs"
onClick={() => {
navigator.clipboard.writeText(processLogs.join('\n'));
navigator.clipboard.writeText(processLogs.join("\n"));
toast({
title: 'Logs copied',
variant: 'success',
id: 'logs',
title: "Logs copied",
variant: "success",
id: "logs",
onDismiss: dismiss,
});
}}
@@ -1,83 +1,83 @@
import React, { ReactNode } from 'react';
import { SiweMessage } from 'siwe';
import { WagmiProvider } from 'wagmi';
import { arbitrum, mainnet } from 'wagmi/chains';
import axios from 'axios';
import React, { ReactNode } from "react";
import { SiweMessage } from "siwe";
import { WagmiProvider } from "wagmi";
import { arbitrum, mainnet } from "wagmi/chains";
import axios from "axios";
import { createWeb3Modal } from '@web3modal/wagmi/react';
import { defaultWagmiConfig } from '@web3modal/wagmi/react/config';
import { createSIWEConfig } from '@web3modal/siwe';
import { createWeb3Modal } from "@web3modal/wagmi/react";
import { defaultWagmiConfig } from "@web3modal/wagmi/react/config";
import { createSIWEConfig } from "@web3modal/siwe";
import type {
SIWECreateMessageArgs,
SIWEVerifyMessageArgs,
} from '@web3modal/core';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
} from "@web3modal/core";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const queryClient = new QueryClient();
const axiosInstance = axios.create({
baseURL: process.env.REACT_APP_SERVER_URL,
baseURL: import.meta.env.VITE_SERVER_URL,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
"Content-Type": "application/json",
"Access-Control-Allow-Origin": "*",
},
withCredentials: true,
});
const metadata = {
name: 'Snowball Tools',
description: 'Snowball Tools Dashboard',
name: "Snowball Tools",
description: "Snowball Tools Dashboard",
url: window.location.origin,
icons: [
'https://raw.githubusercontent.com/snowball-tools/mediakit/main/assets/logo.svg',
"https://raw.githubusercontent.com/snowball-tools/mediakit/main/assets/logo.svg",
],
};
const chains = [mainnet, arbitrum] as const;
const config = defaultWagmiConfig({
chains,
projectId: process.env.REACT_APP_WALLET_CONNECT_ID,
projectId: import.meta.env.VITE_WALLET_CONNECT_ID,
metadata,
});
const siweConfig = createSIWEConfig({
createMessage: ({ nonce, address, chainId }: SIWECreateMessageArgs) =>
new SiweMessage({
version: '1',
version: "1",
domain: window.location.host,
uri: window.location.origin,
address,
chainId,
nonce,
// Human-readable ASCII assertion that the user will sign, and it must not contain `\n`.
statement: 'Sign in With Ethereum.',
statement: "Sign in With Ethereum.",
}).prepareMessage(),
getNonce: async () => {
const nonce = (await axiosInstance.get('/auth/nonce')).data;
const nonce = (await axiosInstance.get("/auth/nonce")).data;
if (!nonce) {
throw new Error('Failed to get nonce!');
throw new Error("Failed to get nonce!");
}
return nonce;
},
getSession: async () => {
try {
const session = (await axiosInstance.get('/auth/session')).data;
const session = (await axiosInstance.get("/auth/session")).data;
const { address, chainId } = session;
return { address, chainId };
} catch (err) {
if (window.location.pathname !== '/login') {
window.location.href = '/login';
if (window.location.pathname !== "/login") {
window.location.href = "/login";
}
throw new Error('Failed to get session!');
throw new Error("Failed to get session!");
}
},
verifyMessage: async ({ message, signature }: SIWEVerifyMessageArgs) => {
try {
const { success } = (
await axiosInstance.post('/auth/validate', {
await axiosInstance.post("/auth/validate", {
message,
signature,
})
@@ -90,28 +90,28 @@ const siweConfig = createSIWEConfig({
},
signOut: async () => {
try {
const { success } = (await axiosInstance.post('/auth/logout')).data;
const { success } = (await axiosInstance.post("/auth/logout")).data;
return success;
} catch (error) {
return false;
}
},
onSignOut: () => {
window.location.href = '/login';
window.location.href = "/login";
},
onSignIn: () => {
window.location.href = '/';
window.location.href = "/";
},
});
if (!process.env.REACT_APP_WALLET_CONNECT_ID) {
throw new Error('Error: REACT_APP_WALLET_CONNECT_ID env config is not set');
if (!import.meta.env.VITE_WALLET_CONNECT_ID) {
throw new Error("Error: REACT_APP_WALLET_CONNECT_ID env config is not set");
}
createWeb3Modal({
siweConfig,
wagmiConfig: config,
projectId: process.env.REACT_APP_WALLET_CONNECT_ID,
projectId: import.meta.env.VITE_WALLET_CONNECT_ID,
});
export default function Web3ModalProvider({
+18 -18
View File
@@ -1,28 +1,28 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import assert from 'assert';
import { GQLClient } from 'gql-client';
import React from "react";
import ReactDOM from "react-dom/client";
import assert from "assert";
import { GQLClient } from "gql-client";
import { ThemeProvider } from '@material-tailwind/react';
import { ThemeProvider } from "@material-tailwind/react";
import './index.css';
import '@fontsource/inter';
import '@fontsource-variable/jetbrains-mono';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { GQLClientProvider } from './context/GQLClientContext';
import { SERVER_GQL_PATH } from './constants';
import { Toaster } from 'components/shared/Toast';
import Web3ModalProvider from './context/Web3ModalProvider';
import "./index.css";
import "@fontsource/inter";
import "@fontsource-variable/jetbrains-mono";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { GQLClientProvider } from "./context/GQLClientContext";
import { SERVER_GQL_PATH } from "./constants";
import { Toaster } from "components/shared/Toast";
import Web3ModalProvider from "./context/Web3ModalProvider";
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement,
document.getElementById("root") as HTMLElement,
);
assert(
process.env.REACT_APP_SERVER_URL,
'REACT_APP_SERVER_URL is not set in env',
import.meta.env.VITE_SERVER_URL,
"REACT_APP_SERVER_URL is not set in env",
);
const gqlEndpoint = `${process.env.REACT_APP_SERVER_URL}/${SERVER_GQL_PATH}`;
const gqlEndpoint = `${import.meta.env.VITE_SERVER_URL}/${SERVER_GQL_PATH}`;
const gqlClient = new GQLClient({ gqlEndpoint });
@@ -0,0 +1,18 @@
import React from 'react';
import { useSnowball } from 'utils/use-snowball';
export const SnowballLogin = () => {
const snowball = useSnowball();
console.log(snowball);
return (
<div className="flex items-center justify-center h-screen bg-snowball-900 snow">
<div className="flex flex-col items-center justify-center">
<img
src="/logo.svg"
alt="snowball logo"
className="w-32 h-32 rounded-full mb-4"
/>
</div>
</div>
);
};
+19
View File
@@ -0,0 +1,19 @@
import { LitGoogleAuth } from "@snowballtools/auth-lit";
import { Snowball, SnowballChain } from "@snowballtools/js-sdk";
// import { LinkLitAlchemyLight } from '@snowballtools/link-lit-alchemy-light';
export const DOMAIN = import.meta.env.VITE_DOMAIN || "localhost";
export const ORIGIN =
import.meta.env.VITE_VERCEL_ENV === "production"
? `https://${DOMAIN}`
: `http://${DOMAIN}:3000`;
// prettier-ignore
export const snowball = Snowball.withAuth(
LitGoogleAuth.configure({
litReplayApiKey: import.meta.env.VITE_LIT_RELAY_API_KEY!,
}),
).create({
initialChain: SnowballChain.sepolia,
});
@@ -0,0 +1,13 @@
import { useEffect, useState } from 'react';
import { snowball } from './snowball';
export function useSnowball() {
const [state, setState] = useState(100);
useEffect(() => {
// Subscribe and directly return the unsubscribe function
return snowball.subscribe(() => setState(state + 1));
}, [snowball]);
return snowball;
}