mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-28 03:35:45 +03:00
add hackathon example
This commit is contained in:
parent
19b931c935
commit
e5cffa228e
6
examples/hackathon/.gitignore
vendored
Normal file
6
examples/hackathon/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
/.wasp/
|
||||
/.env.server
|
||||
/.env.client
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
1
examples/hackathon/.wasproot
Normal file
1
examples/hackathon/.wasproot
Normal file
@ -0,0 +1 @@
|
||||
File marking the root of Wasp project.
|
11
examples/hackathon/README.md
Normal file
11
examples/hackathon/README.md
Normal file
@ -0,0 +1,11 @@
|
||||
<p align=center>
|
||||
<img height="80px" src="https://user-images.githubusercontent.com/1536647/77317442-78625700-6d0b-11ea-9822-0fb21e557e87.png"/>
|
||||
</p>
|
||||
|
||||
# 🐝🚀 Wasp Hackathon Submission App
|
||||
|
||||
This app was created for Wasp's Beta Hackathon (aka Betathon), so that participants would have a central place to learn about the hackathon and submit their projects. The app was deployed to Railway.app and is live [here](https://betathon-production.up.railway.app/).
|
||||
|
||||
Visit the homepage to get started with [Wasp](https://wasp-lang.dev).
|
||||
|
||||
Feel free to use this as a template for your own Hackathons!
|
40
examples/hackathon/main.wasp
Normal file
40
examples/hackathon/main.wasp
Normal file
@ -0,0 +1,40 @@
|
||||
app hackathonBetaSubmissions {
|
||||
wasp: {
|
||||
version: "^0.7.0"
|
||||
},
|
||||
db: {
|
||||
system: PostgreSQL
|
||||
},
|
||||
title: "hackathon-beta-submissions",
|
||||
dependencies: [
|
||||
("react-feather", "2.0.10"),
|
||||
]
|
||||
}
|
||||
|
||||
entity Submission {=psl
|
||||
name String @id @unique
|
||||
email String @unique
|
||||
github String
|
||||
description String
|
||||
twitter String?
|
||||
country String?
|
||||
website String?
|
||||
image String?
|
||||
approved Boolean @default(false)
|
||||
createdAt DateTime @default(now())
|
||||
psl=}
|
||||
|
||||
route RootRoute { path: "/", to: MainPage }
|
||||
page MainPage {
|
||||
component: import Main from "@client/MainPage"
|
||||
}
|
||||
|
||||
action submitProject {
|
||||
fn: import { submitProject } from "@server/actions.js",
|
||||
entities: [Submission]
|
||||
}
|
||||
|
||||
query getProjects {
|
||||
fn: import { getProjects } from "@server/queries.js",
|
||||
entities: [Submission]
|
||||
}
|
8
examples/hackathon/postcss.config.js
Normal file
8
examples/hackathon/postcss.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {
|
||||
config: './tailwind.config.js',
|
||||
},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
3
examples/hackathon/src/.waspignore
Normal file
3
examples/hackathon/src/.waspignore
Normal file
@ -0,0 +1,3 @@
|
||||
# Ignore editor tmp files
|
||||
**/*~
|
||||
**/#*#
|
117
examples/hackathon/src/client/Main.css
Normal file
117
examples/hackathon/src/client/Main.css
Normal file
@ -0,0 +1,117 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
* {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
box-sizing: border-box;
|
||||
|
||||
}
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: Inter;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 4rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
main p {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
.logo {
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.logo img {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.welcome-title {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.welcome-subtitle {
|
||||
font-weight: 400;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.buttons .button:not(:last-child) {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
border-radius: 3px;
|
||||
font-size: 1.2rem;
|
||||
padding: 1rem 2rem;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button-filled {
|
||||
border: 2px solid #bf9900;
|
||||
background-color: #bf9900;
|
||||
color: #f4f4f4;
|
||||
}
|
||||
|
||||
.button-outline {
|
||||
border: 2px solid #8a9cff;
|
||||
color: #8a9cff;
|
||||
background-color: none;
|
||||
}
|
||||
|
||||
code {
|
||||
border-radius: 5px;
|
||||
padding: 0.2rem;
|
||||
background: #efefef;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.submit {
|
||||
font-size: 1rem;
|
||||
/* background-color: #f4e8b3; */
|
||||
/* color: #eab308; */
|
||||
font-weight: 600;
|
||||
/* background-image:
|
||||
linear-gradient(90deg, #bf9900 30%, #121212 100%);
|
||||
background-clip: text;
|
||||
color: transparent; */
|
||||
}
|
||||
|
||||
.submit:hover {
|
||||
background-color: #fcf3d0;
|
||||
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
background-image:
|
||||
linear-gradient(90deg, #bf9900 30%, #646464 100%);
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: 1px solid #bf9900;
|
||||
}
|
27
examples/hackathon/src/client/MainPage.tsx
Normal file
27
examples/hackathon/src/client/MainPage.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import React from 'react';
|
||||
import betathonLogo from './betathonLogo.png';
|
||||
import './Main.css';
|
||||
import Nav from './components/Navbar';
|
||||
import Form from './components/SubmissionForm';
|
||||
import Projects from './components/Projects';
|
||||
|
||||
const MainPage = () => {
|
||||
return (
|
||||
<div>
|
||||
<Nav />
|
||||
<main>
|
||||
<img
|
||||
alt='betathon logo'
|
||||
src={betathonLogo}
|
||||
className=' mb-16 shadow-lg border-2 rounded-md border-yellow-500/25'
|
||||
width={600}
|
||||
height={600}
|
||||
/>
|
||||
|
||||
<Projects />
|
||||
<Form />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default MainPage;
|
BIN
examples/hackathon/src/client/betathonLogo.png
Normal file
BIN
examples/hackathon/src/client/betathonLogo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
177
examples/hackathon/src/client/components/Navbar.tsx
Normal file
177
examples/hackathon/src/client/components/Navbar.tsx
Normal file
@ -0,0 +1,177 @@
|
||||
import React from 'react';
|
||||
import waspLogo from '../waspLogo.png';
|
||||
import { Star } from 'react-feather';
|
||||
|
||||
export const DiscordIcon = () => (
|
||||
<svg width='24' height='24' fill='currentColor' viewBox='0 5 30.67 23.25'>
|
||||
<title>Discord</title>
|
||||
<path d='M26.0015 6.9529C24.0021 6.03845 21.8787 5.37198 19.6623 5C19.3833 5.48048 19.0733 6.13144 18.8563 6.64292C16.4989 6.30193 14.1585 6.30193 11.8336 6.64292C11.6166 6.13144 11.2911 5.48048 11.0276 5C8.79575 5.37198 6.67235 6.03845 4.6869 6.9529C0.672601 12.8736 -0.41235 18.6548 0.130124 24.3585C2.79599 26.2959 5.36889 27.4739 7.89682 28.2489C8.51679 27.4119 9.07477 26.5129 9.55525 25.5675C8.64079 25.2265 7.77283 24.808 6.93587 24.312C7.15286 24.1571 7.36986 23.9866 7.57135 23.8161C12.6241 26.1255 18.0969 26.1255 23.0876 23.8161C23.3046 23.9866 23.5061 24.1571 23.7231 24.312C22.8861 24.808 22.0182 25.2265 21.1037 25.5675C21.5842 26.5129 22.1422 27.4119 22.7621 28.2489C25.2885 27.4739 27.8769 26.2959 30.5288 24.3585C31.1952 17.7559 29.4733 12.0212 26.0015 6.9529ZM10.2527 20.8402C8.73376 20.8402 7.49382 19.4608 7.49382 17.7714C7.49382 16.082 8.70276 14.7025 10.2527 14.7025C11.7871 14.7025 13.0425 16.082 13.0115 17.7714C13.0115 19.4608 11.7871 20.8402 10.2527 20.8402ZM20.4373 20.8402C18.9183 20.8402 17.6768 19.4608 17.6768 17.7714C17.6768 16.082 18.8873 14.7025 20.4373 14.7025C21.9717 14.7025 23.2271 16.082 23.1961 17.7714C23.1961 19.4608 21.9872 20.8402 20.4373 20.8402Z'></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const TwitterIcon = () => (
|
||||
<svg width='24' height='24' fill='currentColor' viewBox='0 0 335 276'>
|
||||
<title>Twitter</title>
|
||||
<path d='m302 70a195 195 0 0 1 -299 175 142 142 0 0 0 97 -30 70 70 0 0 1 -58 -47 70 70 0 0 0 31 -2 70 70 0 0 1 -57 -66 70 70 0 0 0 28 5 70 70 0 0 1 -18 -90 195 195 0 0 0 141 72 67 67 0 0 1 116 -62 117 117 0 0 0 43 -17 65 65 0 0 1 -31 38 117 117 0 0 0 39 -11 65 65 0 0 1 -32 35'></path>
|
||||
</svg>
|
||||
);
|
||||
|
||||
const Navbar = () => {
|
||||
|
||||
function scrollToTargetAdjusted() {
|
||||
var element = document.getElementById('submission');
|
||||
var headerOffset = 75;
|
||||
var elementPosition = element.getBoundingClientRect().top;
|
||||
var offsetPosition = elementPosition + window.pageYOffset - headerOffset;
|
||||
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}
|
||||
|
||||
const Logo = () => (
|
||||
<div className='flex flex-shrink-0 items-center'>
|
||||
<a href='https://www.wasp-lang.dev'>
|
||||
<img src={waspLogo} width={35} height={35} alt='Wasp Logo' />
|
||||
</a>
|
||||
<span className='hidden md:block ml-3 font-semibold text-lg text-neutral-700'>
|
||||
Wasp <sup className='text-base text-yellow-500'>βetathon</sup>
|
||||
</span>
|
||||
<span className='sm:hidden text-base ml-3 font-semibold text-yellow-500'>βetathon</span>
|
||||
</div>
|
||||
);
|
||||
|
||||
const SocialIcon = ({ Icon, url }) => (
|
||||
<a
|
||||
href={url}
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={`
|
||||
lg:flex hover:opacity-75 py-5
|
||||
hover:text-yellow-500 hover:border-yellow-500
|
||||
border-b-2 border-transparent
|
||||
`}
|
||||
>
|
||||
<Icon />
|
||||
</a>
|
||||
);
|
||||
|
||||
const GitHubButton = () => (
|
||||
<a
|
||||
href='https://github.com/wasp-lang/wasp'
|
||||
target='_blank'
|
||||
rel='noreferrer'
|
||||
className={`
|
||||
px-2.5 py-1 rounded
|
||||
hover:bg-neutral-200
|
||||
transition ease-out duration-200
|
||||
lg:flex items-center space-x-2 text-xs
|
||||
group
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
flex h-3 w-3 items-center justify-center
|
||||
group-hover:h-4 group-hover:w-4
|
||||
group-hover:text-yellow-500
|
||||
`}
|
||||
>
|
||||
<Star strokeWidth={2} />
|
||||
</div>
|
||||
<span className='truncate'>Star us on GitHub</span>
|
||||
</a>
|
||||
);
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='sticky top-0 z-50'>
|
||||
<div className='bg-[#f5f4f0] absolute top-0 h-full w-full opacity-80'></div>
|
||||
<nav className='border-b backdrop-blur-sm'>
|
||||
<div
|
||||
className='
|
||||
relative mx-auto
|
||||
flex justify-between
|
||||
h-16
|
||||
container lg:px-20 md:px-16 pl-2
|
||||
'
|
||||
>
|
||||
<div
|
||||
className='
|
||||
flex flex-1
|
||||
items-center justify-center
|
||||
justify-between
|
||||
|
||||
'
|
||||
>
|
||||
<div className='flex flex-1 items-center'>
|
||||
{' '}
|
||||
{/* Navbar left side */}
|
||||
<Logo />
|
||||
<div className='pl-2 sm:ml-6 sm:space-x-4 lg:flex'>
|
||||
<a href='https://wasp-lang.notion.site/Wasp-Betathon-f68015a68a15419e978c0648031a8634'>
|
||||
<span
|
||||
className={`
|
||||
py-5 px-1
|
||||
hover:text-yellow-500 hover:border-yellow-500
|
||||
border-b-solid border-b-2 border-transparent
|
||||
text-sm font-semibold
|
||||
`}
|
||||
>
|
||||
Rules
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>{' '}
|
||||
<div className='flex flex-1 items-center justify-center border-transparent'>
|
||||
{/* Submit Project */}
|
||||
<button onClick={scrollToTargetAdjusted} className='outline-none focus:outline-none'>
|
||||
<span
|
||||
className={`
|
||||
py-5 px-1
|
||||
border-b-2 border-transparent
|
||||
text-base font-medium
|
||||
`}
|
||||
>
|
||||
<div className='relative inline-flex items-center px-3 py-2 bg-gray-100 rounded border border-neutral-500 text-neutral-700 hover:text-neutral-400 hover:border-neutral-400 hover:animate-pulse transition ease-out duration-200'>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='16'
|
||||
height='16'
|
||||
viewBox='0 0 24 24'
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
>
|
||||
<polyline points='4 17 10 11 4 5'></polyline>
|
||||
<line x1='12' y1='19' x2='20' y2='19'></line>
|
||||
</svg>{' '}
|
||||
<span className='pl-2 gradient-text hover:text-neutral-400 transition ease-out duration-200'>
|
||||
Submit a Project
|
||||
</span>
|
||||
</div>
|
||||
</span>
|
||||
</button>
|
||||
</div>{' '}
|
||||
{/* EOF left side */}
|
||||
<div className='hidden md:flex flex-1 items-center justify-end gap-2 space-x-2'>
|
||||
{' '}
|
||||
{/* Navbar right side */}
|
||||
<GitHubButton />
|
||||
<SocialIcon Icon={DiscordIcon} url='https://discord.gg/rzdnErX' />
|
||||
<SocialIcon Icon={TwitterIcon} url='https://twitter.com/WaspLang' />
|
||||
</div>{' '}
|
||||
{/* EOF right side */}
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
67
examples/hackathon/src/client/components/Projects.tsx
Normal file
67
examples/hackathon/src/client/components/Projects.tsx
Normal file
@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { useQuery } from '@wasp/queries';
|
||||
import getProjects from '@wasp/queries/getProjects';
|
||||
import betathonLogo from '../betathonLogo.png';
|
||||
|
||||
const Projects = () => {
|
||||
const { data: projects, status } = useQuery(getProjects);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='bg-neutral-100'>
|
||||
<div className='mx-auto max-w-7xl px-4 sm:px-6 lg:px-8'>
|
||||
<div className='mx-auto max-w-2xl pb-16 lg:max-w-none'>
|
||||
<h2 className='text-2xl text-neutral-700 underline decoration-yellow-500 text-center'>
|
||||
Submitted Projects{' '}
|
||||
</h2>
|
||||
<div className='mt-12 gap-6 lg:grid lg:grid-cols-3'>
|
||||
{status === 'success' && projects.length ? (
|
||||
projects.map((project) => (
|
||||
<div key={project.name} className='group relative'>
|
||||
<div className='relative h-80 w-full overflow-hidden rounded-lg bg-white group-hover:opacity-75 sm:aspect-w-2 sm:aspect-h-1 sm:h-64 lg:aspect-w-1 lg:aspect-h-1'>
|
||||
<img
|
||||
src={project.image || betathonLogo}
|
||||
alt={project.name}
|
||||
className='h-full w-full object-cover object-center'
|
||||
/>
|
||||
</div>
|
||||
<h3 className='mt-6 text-base font-semibold text-neutral-700'>
|
||||
<a href={!!project.website ? '//' + project.website : null} target='_blank' rel='noreferrer'>
|
||||
<span className='absolute inset-0' />
|
||||
{project.name}
|
||||
</a>
|
||||
</h3>
|
||||
<p className='text-sm text-neutral-700'>
|
||||
{project.description.length > 120
|
||||
? project.description.substring(0, 142).concat('...')
|
||||
: project.description}
|
||||
</p>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className='col-span-3 border border-yellow-500/25 rounded-md bg-yellow-100 bg-opacity-10 shadow-md p-5 text-center justify-self-center'>
|
||||
<p className='font-semibold'>Nothing to see here... yet</p>
|
||||
<p className='text-sm mt-4'>
|
||||
Looking for some inspiration? <br />
|
||||
Check out our{' '}
|
||||
<a
|
||||
className='text-yellow-600 underline decoration-neutral-700 '
|
||||
href='https://github.com/wasp-lang/wasp/tree/main/examples'
|
||||
>
|
||||
{' '}
|
||||
Example Wasp Apps
|
||||
</a>{' '}
|
||||
<p className='text-sm mt-4'>
|
||||
BTW, This is also a Wasp App! 🧙♂️
|
||||
</p>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default Projects;
|
285
examples/hackathon/src/client/components/SubmissionForm.tsx
Normal file
285
examples/hackathon/src/client/components/SubmissionForm.tsx
Normal file
@ -0,0 +1,285 @@
|
||||
import React, { useState } from 'react';
|
||||
import submitProject from '@wasp/actions/submitProject';
|
||||
|
||||
export type Submission = {
|
||||
name: string;
|
||||
email: string;
|
||||
country: string;
|
||||
website: string;
|
||||
github: string;
|
||||
twitter: string;
|
||||
description: string;
|
||||
image: string;
|
||||
};
|
||||
|
||||
const SubmissionForm = () => {
|
||||
const [file, setFile] = useState<any>();
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
const [imageLink, setImageLink] = useState('');
|
||||
|
||||
const onFileUpload = async (event) => {
|
||||
setIsUploading(true);
|
||||
const clientId = 'd4ecb4220cf055b'
|
||||
const auth = 'Client-ID ' + clientId;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('image', event.target?.files[0]);
|
||||
|
||||
try {
|
||||
const imgur = await fetch('https://api.imgur.com/3/upload', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
Authorization: auth,
|
||||
Accept: 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
const json = await imgur.json();
|
||||
|
||||
if (!json.success) {
|
||||
throw new Error('Image upload failed');
|
||||
}
|
||||
setFile(event.target.files[0].name);
|
||||
setImageLink(json.data.link);
|
||||
} catch (error) {
|
||||
console.error('error uploading image');
|
||||
}
|
||||
setIsUploading(false);
|
||||
};
|
||||
|
||||
const handleSubmit = async (event) => {
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.target);
|
||||
const value = Object.fromEntries(data.entries());
|
||||
delete value['file-upload'];
|
||||
value.image = imageLink;
|
||||
|
||||
try {
|
||||
await submitProject(value as Submission);
|
||||
alert('Project submitted successfully! It will be visible once it is approved.');
|
||||
event.target.reset();
|
||||
} catch (e) {
|
||||
console.error('Error while submitting project', e);
|
||||
alert('Error while submitting project');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='skew-y-min2 border-double border-2 border-t border-b border-yellow-500/25 w-full mb-10'></div>
|
||||
|
||||
<div className='relative mt-10 md:pt-6 px-3 sm:mt-0 sm:px-20 sm:mx-10 lg:w-1/2' id='submission'>
|
||||
<h2 className='text-2xl text-neutral-700 mb-5 mx-1 underline decoration-yellow-500'>Submit a Project</h2>
|
||||
|
||||
<div className='md:grid md:grid-cols-2 md:gap-6'>
|
||||
<div className='mt-5 md:col-span-2 md:mt-0'>
|
||||
<form onSubmit={handleSubmit} method='POST'>
|
||||
<div className='overflow-hidden shadow sm:rounded-md'>
|
||||
<div className='bg-white px-4 py-5 sm:p-6'>
|
||||
<div className='grid grid-cols-6 gap-6'>
|
||||
<div className='col-span-6'>
|
||||
<label htmlFor='name' className='block text-sm font-medium text-gray-700'>
|
||||
Project Name *
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
name='name'
|
||||
id='name'
|
||||
required
|
||||
className='mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 sm:text-sm'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='col-span-6'>
|
||||
<label htmlFor='email' className='block text-sm font-medium text-gray-700'>
|
||||
Email address *
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
name='email'
|
||||
id='email'
|
||||
required
|
||||
autoComplete='email'
|
||||
className='mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 sm:text-sm'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='col-span-6'>
|
||||
<label htmlFor='country' className='block text-sm font-medium text-gray-700'>
|
||||
Countries Represented
|
||||
</label>
|
||||
<input
|
||||
type='text'
|
||||
name='country'
|
||||
id='country'
|
||||
autoComplete='country-name'
|
||||
className='mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 sm:text-sm'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='col-span-6'>
|
||||
<label htmlFor='github' className='block text-sm font-medium text-gray-700'>
|
||||
GitHub Repo *
|
||||
</label>
|
||||
<div className='mt-1 flex rounded-md shadow-sm'>
|
||||
<span className='inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500'>
|
||||
http://
|
||||
</span>
|
||||
<input
|
||||
type='text'
|
||||
name='github'
|
||||
id='github'
|
||||
required
|
||||
className='block w-full flex-1 rounded-none rounded-r-md border border-gray-300 focus:border-yellow-500 focus:ring-yellow-500 sm:text-sm'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='col-span-6'>
|
||||
<label htmlFor='website' className='block text-sm font-medium text-gray-700'>
|
||||
Website
|
||||
</label>
|
||||
<div className='mt-1 flex rounded-md shadow-sm'>
|
||||
<span className='inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500'>
|
||||
http://
|
||||
</span>
|
||||
<input
|
||||
type='text'
|
||||
name='website'
|
||||
id='website'
|
||||
className='block w-full flex-1 rounded-none rounded-r-md border border-gray-300 focus:border-yellow-500 focus:ring-yellow-500 sm:text-sm'
|
||||
placeholder='www.example.com'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='col-span-6'>
|
||||
<label htmlFor='twitter' className='block text-sm font-medium text-gray-700'>
|
||||
Twitter
|
||||
</label>
|
||||
<div className='mt-1 flex rounded-md shadow-sm'>
|
||||
<span className='inline-flex items-center rounded-l-md border border-r-0 border-gray-300 bg-gray-50 px-3 text-sm text-gray-500'>
|
||||
http://
|
||||
</span>
|
||||
<input
|
||||
type='text'
|
||||
name='twitter'
|
||||
id='twitter'
|
||||
className='block w-full flex-1 rounded-none rounded-r-md border border-gray-300 focus:border-yellow-500 focus:ring-yellow-500 sm:text-sm'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='col-span-6'>
|
||||
<label htmlFor='description' className='block text-sm font-medium text-gray-700'>
|
||||
Description *
|
||||
</label>
|
||||
<div className='mt-1'>
|
||||
<textarea
|
||||
id='description'
|
||||
name='description'
|
||||
rows={3}
|
||||
className='mt-1 block w-full rounded-md border border-gray-300 shadow-sm focus:border-yellow-500 focus:ring-yellow-500 sm:text-sm'
|
||||
placeholder='Our project is so WASPY because...'
|
||||
required
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='col-span-6'>
|
||||
<label className='block text-sm font-medium text-gray-700'>Cover photo</label>
|
||||
<div
|
||||
className={`${
|
||||
isUploading && 'pointer-events-none opacity-35'
|
||||
} mt-1 flex justify-center rounded-md border-2 border-dashed border-gray-300 px-6 pt-5 pb-6`}
|
||||
>
|
||||
<div className={`${isUploading && 'animate-pulse'} space-y-1 text-center`}>
|
||||
<svg
|
||||
className='mx-auto h-12 w-12 text-gray-400'
|
||||
stroke='currentColor'
|
||||
fill='none'
|
||||
viewBox='0 0 48 48'
|
||||
aria-hidden='true'
|
||||
>
|
||||
<path
|
||||
d='M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02'
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
</svg>
|
||||
<div className='flex text-sm text-gray-600 justify-center'>
|
||||
<label
|
||||
htmlFor='file-upload'
|
||||
className='relative cursor-pointer rounded-md bg-white font-medium underline text-yellow-600 focus-within:outline-none hover:text-yellow-500 outline-none focus:outline-none'
|
||||
>
|
||||
{/** UPLOAD IMAGE */}
|
||||
{isUploading ? (
|
||||
<svg
|
||||
className='animate-spin -ml-1 mr-3 h-5 w-5 text-white'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='none'
|
||||
viewBox='0 0 24 24'
|
||||
>
|
||||
<circle
|
||||
className='opacity-25'
|
||||
cx='12'
|
||||
cy='12'
|
||||
r='10'
|
||||
stroke='black'
|
||||
strokeWidth='4'
|
||||
></circle>
|
||||
<path
|
||||
className='opacity-75'
|
||||
fill='gray'
|
||||
d='M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z'
|
||||
></path>
|
||||
</svg>
|
||||
) : (
|
||||
<span>Upload an Image</span>
|
||||
)}
|
||||
<input
|
||||
id='file-upload'
|
||||
name='file-upload'
|
||||
type='file'
|
||||
onChange={(e) => onFileUpload(e)}
|
||||
className='sr-only'
|
||||
accept='image/png, image/jpeg, image/jpg'
|
||||
/>
|
||||
</label>
|
||||
{!!file && (
|
||||
<p className='pl-1'>{file} uploaded</p>
|
||||
)}
|
||||
</div>
|
||||
<p className='text-xs text-gray-500'>PNG, JPG, GIF up to 10MB</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='bg-gray-50 px-4 py-3 text-right sm:px-6'>
|
||||
<button
|
||||
type='submit'
|
||||
disabled={isUploading}
|
||||
className='inline-flex justify-center rounded-md border border-transparent bg-yellow-600 py-2 px-4 text-sm font-medium text-white shadow-sm disabled:opacity-60 enabled:hover:bg-yellow-700 focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:ring-offset-2'
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='hidden sm:block' aria-hidden='true'>
|
||||
<div className='py-5'>
|
||||
<div className='border-t border-gray-200'></div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubmissionForm;
|
60
examples/hackathon/src/client/react-app-env.d.ts
vendored
Normal file
60
examples/hackathon/src/client/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
declare module '*.avif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.bmp' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.gif' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.jpg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.jpeg' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.png' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.webp' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
import * as React from 'react';
|
||||
|
||||
export const ReactComponent: React.FunctionComponent<React.SVGProps<
|
||||
SVGSVGElement
|
||||
> & { title?: string }>;
|
||||
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.module.css' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.scss' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
declare module '*.module.sass' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
39
examples/hackathon/src/client/tsconfig.json
Normal file
39
examples/hackathon/src/client/tsconfig.json
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// JSX support
|
||||
"jsx": "preserve",
|
||||
// Enable default imports in TypeScript.
|
||||
"esModuleInterop": true,
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
// The following settings enable IDE support in user-provided source files.
|
||||
// Editing them might break features like import autocompletion and
|
||||
// definition lookup. Don't change them unless you know what you're doing.
|
||||
//
|
||||
// The relative path to the generated web app's root directory. This must be
|
||||
// set to define the "paths" option.
|
||||
"baseUrl": "../../.wasp/out/web-app/",
|
||||
"paths": {
|
||||
// Resolve all "@wasp" imports to the generated source code.
|
||||
"@wasp/*": [
|
||||
"src/*"
|
||||
],
|
||||
// Resolve all non-relative imports to the correct node module. Source:
|
||||
// https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
|
||||
"*": [
|
||||
// Start by looking for the definiton inside the node modules root
|
||||
// directory...
|
||||
"node_modules/*",
|
||||
// ... If that fails, try to find it inside definitely-typed type
|
||||
// definitions.
|
||||
"node_modules/@types/*"
|
||||
]
|
||||
},
|
||||
// Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots
|
||||
"typeRoots": ["../../.wasp/out/web-app/node_modules/@types"]
|
||||
}
|
||||
}
|
BIN
examples/hackathon/src/client/waspLogo.png
Normal file
BIN
examples/hackathon/src/client/waspLogo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
8
examples/hackathon/src/server/actions.js
Normal file
8
examples/hackathon/src/server/actions.js
Normal file
@ -0,0 +1,8 @@
|
||||
export const submitProject = async (project, context) => {
|
||||
|
||||
const newProject = context.entities.Submission.create({
|
||||
data: project,
|
||||
});
|
||||
|
||||
return newProject;
|
||||
};
|
11
examples/hackathon/src/server/queries.js
Normal file
11
examples/hackathon/src/server/queries.js
Normal file
@ -0,0 +1,11 @@
|
||||
export const getProjects = async (args, context) => {
|
||||
|
||||
return context.entities.Submission.findMany({
|
||||
where: {
|
||||
approved: true,
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc',
|
||||
},
|
||||
});
|
||||
};
|
32
examples/hackathon/src/server/tsconfig.json
Normal file
32
examples/hackathon/src/server/tsconfig.json
Normal file
@ -0,0 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable default imports in TypeScript.
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
// The following settings enable IDE support in user-provided source files.
|
||||
// Editing them might break features like import autocompletion and
|
||||
// definition lookup. Don't change them unless you know what you're doing.
|
||||
//
|
||||
// The relative path to the generated web app's root directory. This must be
|
||||
// set to define the "paths" option.
|
||||
"baseUrl": "../../.wasp/out/server/",
|
||||
"paths": {
|
||||
// Resolve all "@wasp" imports to the generated source code.
|
||||
"@wasp/*": [
|
||||
"src/*"
|
||||
],
|
||||
// Resolve all non-relative imports to the correct node module. Source:
|
||||
// https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
|
||||
"*": [
|
||||
// Start by looking for the definiton inside the node modules root
|
||||
// directory...
|
||||
"node_modules/*",
|
||||
// ... If that fails, try to find it inside definitely-typed type
|
||||
// definitions.
|
||||
"node_modules/@types/*"
|
||||
]
|
||||
},
|
||||
// Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots
|
||||
"typeRoots": ["../../.wasp/out/server/node_modules/@types"]
|
||||
}
|
||||
}
|
28
examples/hackathon/src/shared/tsconfig.json
Normal file
28
examples/hackathon/src/shared/tsconfig.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
// Enable default imports in TypeScript.
|
||||
"esModuleInterop": true,
|
||||
"allowJs": true,
|
||||
// The following settings enable IDE support in user-provided source files.
|
||||
// Editing them might break features like import autocompletion and
|
||||
// definition lookup. Don't change them unless you know what you're doing.
|
||||
//
|
||||
// The relative path to the generated web app's root directory. This must be
|
||||
// set to define the "paths" option.
|
||||
"baseUrl": "../../.wasp/out/server/",
|
||||
"paths": {
|
||||
// Resolve all non-relative imports to the correct node module. Source:
|
||||
// https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
|
||||
"*": [
|
||||
// Start by looking for the definiton inside the node modules root
|
||||
// directory...
|
||||
"node_modules/*",
|
||||
// ... If that fails, try to find it inside definitely-typed type
|
||||
// definitions.
|
||||
"node_modules/@types/*"
|
||||
]
|
||||
},
|
||||
// Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots
|
||||
"typeRoots": ["../../.wasp/out/server/node_modules/@types"]
|
||||
}
|
||||
}
|
19
examples/hackathon/tailwind.config.js
Normal file
19
examples/hackathon/tailwind.config.js
Normal file
@ -0,0 +1,19 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{js,ts,jsx,tsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
skew: {
|
||||
'min2': '-2deg',
|
||||
'min4': '-4deg',
|
||||
'min6': '-6deg',
|
||||
}
|
||||
},
|
||||
focus: {
|
||||
outline: 'none',
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
// require('@tailwindcss/aspect-ratio')
|
||||
],
|
||||
};
|
Loading…
Reference in New Issue
Block a user