Compare commits
2 commits
4895d3b889
...
22f59ca619
Author | SHA1 | Date | |
---|---|---|---|
|
22f59ca619 | ||
|
882dd995f9 |
6 changed files with 204 additions and 127 deletions
47
app/components/form.tsx
Normal file
47
app/components/form.tsx
Normal file
|
@ -0,0 +1,47 @@
|
|||
'use client';
|
||||
import { useState } from "react";
|
||||
|
||||
const Form = <T,>({ initialData, onSubmit }) => {
|
||||
const [formData, setFormData] = useState( initialData );
|
||||
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
//q = formData;
|
||||
|
||||
//addQuestion( q );
|
||||
|
||||
//console.log( `Adding question ${JSON.stringify(q)}` );
|
||||
|
||||
onSubmit( formData );
|
||||
}
|
||||
|
||||
const handleChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setFormData( (prevData) => ({
|
||||
...prevData,
|
||||
[name]: value
|
||||
}));
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit} className="flex">
|
||||
<div>
|
||||
{Object.entries( initialData ).map( (key, val) => {
|
||||
return(
|
||||
<input
|
||||
type = { typeof( val ) == typeof( 0 ) ? "number" : "text" /* pretty hacky, we should change this */ }
|
||||
id = {key[0]}
|
||||
name = {key[0]}
|
||||
value = {formData[key[0]]}
|
||||
onChange = {handleChange}
|
||||
required
|
||||
/>)})}
|
||||
</div>
|
||||
<button type="submit">Submit</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
export default Form;
|
||||
|
44
app/components/topic_card.tsx
Normal file
44
app/components/topic_card.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
|
||||
const TopicCard = ({ title, children, link }) => {
|
||||
return(
|
||||
<div className="mx-auto mt-16 max-w-2xl rounded-3xl sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx={512} cy={512} r={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
|
||||
<defs>
|
||||
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
||||
<stop stopColor="#7775D6" />
|
||||
<stop offset={1} stopColor="#E935C1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div className="p-8 sm:p-10 lg:flex-auto ">
|
||||
<h3 className="text-2xl font-bold tracking-tight text-gray-200">{title}</h3>
|
||||
<p className="mt-6 text-base leading-7 text-gray-200">
|
||||
{children}
|
||||
</p>
|
||||
</div>
|
||||
<div className="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div className="rounded-2xl bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div className="mx-auto max-w-xs px-8">
|
||||
<p className="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<span className="text-5xl font-bold tracking-tight text-gray-900">Start</span>
|
||||
<span className="text-sm font-semibold leading-6 tracking-wide text-gray-900">now!</span>
|
||||
</p>
|
||||
<a
|
||||
href={link}
|
||||
className="mt-10 block w-full rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||
>
|
||||
Begin
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default TopicCard;
|
33
app/lib/api.ts
Normal file
33
app/lib/api.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
// the base URL of the api; all requests start with it
|
||||
const base_url: string = "http://localhost:4000/";
|
||||
|
||||
/*
|
||||
export const makeGetRequest = async<T> (path: string): Promise<T> => {
|
||||
return await (await fetch( base_url + path )).json();
|
||||
}
|
||||
*/
|
||||
|
||||
// generic function that makes a web request
|
||||
const makeRequest = async (path: string, method: string, body?: string): Promise<Response> => {
|
||||
console.log( `making ${method} request: ${base_url + path}, ${body}` );
|
||||
return fetch( base_url + path, { method: method, body: body ? body : null } );
|
||||
}
|
||||
|
||||
// the following functions are wrappers around `makeRequest`,
|
||||
// for every type of request that's needed.
|
||||
|
||||
export const makeGetRequest = async<T> (path: string): Promise<T> => {
|
||||
return (await makeRequest( path, "GET" )).json();
|
||||
}
|
||||
|
||||
export const makePostRequest = async<T> (path: string, body: T): Promise<T> => {
|
||||
return (await makeRequest( path, "POST", JSON.stringify( body ) )).json();
|
||||
}
|
||||
|
||||
export const makePutRequest = async<T> (path: string, body: T): Promise<T> => {
|
||||
return (await makeRequest( path, "PUT", JSON.stringify( body ) )).json();
|
||||
}
|
||||
|
||||
export const makeDeleteRequest = async<T> (path: string): Promise<T> => {
|
||||
return (await makeRequest( path, "DELETE" )).json();
|
||||
}
|
33
app/lib/question.ts
Normal file
33
app/lib/question.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { makeDeleteRequest, makeGetRequest, makePostRequest } from "./api";
|
||||
|
||||
export const enum QuizQuestion {
|
||||
UL,
|
||||
UR,
|
||||
DL,
|
||||
DR
|
||||
};
|
||||
|
||||
export type Question = {
|
||||
id: number;
|
||||
category: string | null;
|
||||
type: string | null; // TODO: make a TS type for every question type
|
||||
text: string;
|
||||
answer: string | boolean | QuizQuestion;
|
||||
}
|
||||
|
||||
export const getQuestion = async (id: number): Promise<Question> => {
|
||||
return makeGetRequest( `questions/${id}/` );
|
||||
}
|
||||
|
||||
export const addQuestion = async (question: Question): Promise<Question> => {
|
||||
console.log( `adding question ${JSON.stringify( question)}` );
|
||||
return makePostRequest( `questions`, question );
|
||||
}
|
||||
|
||||
export const editQuestion = async (question: Question): Promise<Question> => {
|
||||
return makePostRequest( `questions/${question.id}`, question );
|
||||
}
|
||||
|
||||
export const deleteQuestion = async (id: number): Promise<Question> => {
|
||||
return makeDeleteRequest( `questions/${id}` );
|
||||
}
|
17
app/lib/session.ts
Normal file
17
app/lib/session.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { makeGetRequest, makePostRequest } from "./api";
|
||||
|
||||
export type Session = {
|
||||
id: number;
|
||||
right: number;
|
||||
wrong: number;
|
||||
total: number;
|
||||
// user: string;
|
||||
}
|
||||
|
||||
export const getSession = async (id: number): Promise<Session> => {
|
||||
return makeGetRequest( `sessions/${id}/` );
|
||||
}
|
||||
|
||||
export const postSession = async (session: Session): Promise<Session> => {
|
||||
return makePostRequest( `sessions`, session );
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
import { CheckIcon } from '@heroicons/react/20/solid'
|
||||
import TopicCard from '../components/topic_card'
|
||||
|
||||
const includedFeatures = [
|
||||
'Private forum access',
|
||||
|
@ -8,136 +9,38 @@ const includedFeatures = [
|
|||
]
|
||||
|
||||
export default function Example() {
|
||||
|
||||
const cards = [
|
||||
["Learn cybersecurity", "Learning cybersecurity is like becoming a guardian of the digital realm.\nIt's about mastering the art of protecting information, systems, and networks from cyber threats.\nFrom understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.", "/learn/cybersec" ],
|
||||
["Learn geography", "It's cool ig", "/learn/geography"],
|
||||
["Learn C++", "Don't.", "/learn/cpp"],
|
||||
["Learn C", "Do.", "/learn/c"]
|
||||
];
|
||||
return (
|
||||
|
||||
<div className="bg-black py-24 sm:py-32">
|
||||
<div style={{ display:"flex", alignItems: 'center', justifyContent: 'center', paddingTop:'40px'}}><img className="flex" src="/logo-no-background.png" height="auto" width="40%" style={{ alignSelf: 'center'}}></img></div>
|
||||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||||
<div className="mx-auto mt-16 max-w-2xl rounded-3xl ring-1 ring-gray-200 sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx={512} cy={512} r={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
|
||||
<defs>
|
||||
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
||||
<stop stopColor="#7775D6" />
|
||||
<stop offset={1} stopColor="#E935C1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div className="p-8 sm:p-10 lg:flex-auto ">
|
||||
<h3 className="text-2xl font-bold tracking-tight text-gray-200">Learn cybersecurity</h3>
|
||||
<p className="mt-6 text-base leading-7 text-gray-200">
|
||||
Learning cybersecurity is like becoming the guardian of the digital realm.
|
||||
{ cards.map((elem) => {
|
||||
return (<TopicCard title={elem[0]} link={elem[2]}>{elem[1]}</TopicCard>)
|
||||
})}
|
||||
{/*
|
||||
<TopicCard title = "Learn cybersecurity">
|
||||
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||
</p>
|
||||
</div>
|
||||
<div className="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div className="rounded-2xl bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div className="mx-auto max-w-xs px-8">
|
||||
<p className="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<span className="text-5xl font-bold tracking-tight text-gray-900">Start</span>
|
||||
<span className="text-sm font-semibold leading-6 tracking-wide text-gray-900">now!</span>
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
className="mt-10 block w-full rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||
>
|
||||
Begin
|
||||
</a>
|
||||
<p className="mt-6 text-xs leading-5 text-gray-600">
|
||||
Take a short quiz to begin learning faster than ever
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-16 max-w-2xl rounded-3xl ring-1 ring-gray-200 sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx={512} cy={512} r={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
|
||||
<defs>
|
||||
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
||||
<stop stopColor="#7775D6" />
|
||||
<stop offset={1} stopColor="#E935C1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div className="p-8 sm:p-10 lg:flex-auto ">
|
||||
<h3 className="text-2xl font-bold tracking-tight text-gray-200">Learn cybersecurity</h3>
|
||||
<p className="mt-6 text-base leading-7 text-gray-200">
|
||||
Learning cybersecurity is like becoming the guardian of the digital realm.
|
||||
</TopicCard>
|
||||
<TopicCard title = "Learn geography">
|
||||
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||
</p>
|
||||
</div>
|
||||
<div className="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div className="rounded-2xl bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div className="mx-auto max-w-xs px-8">
|
||||
<p className="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<span className="text-5xl font-bold tracking-tight text-gray-900">Start</span>
|
||||
<span className="text-sm font-semibold leading-6 tracking-wide text-gray-900">now!</span>
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
className="mt-10 block w-full rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||
>
|
||||
Begin
|
||||
</a>
|
||||
<p className="mt-6 text-xs leading-5 text-gray-600">
|
||||
Take a short quiz to begin learning faster than ever
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-auto mt-16 max-w-2xl rounded-3xl ring-1 ring-gray-200 sm:mt-20 lg:mx-0 lg:flex lg:max-w-none relative isolate overflow-hidden bg-gray-900 px-6 pt-16 shadow-2xl sm:rounded-3xl sm:px-16 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0">
|
||||
<svg
|
||||
viewBox="0 0 1024 1024"
|
||||
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<circle cx={512} cy={512} r={512} fill="url(#759c1415-0410-454c-8f7c-9a820de03641)" fillOpacity="0.7" />
|
||||
<defs>
|
||||
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
|
||||
<stop stopColor="#7775D6" />
|
||||
<stop offset={1} stopColor="#E935C1" />
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<div className="p-8 sm:p-10 lg:flex-auto ">
|
||||
<h3 className="text-2xl font-bold tracking-tight text-gray-200">Learn cybersecurity</h3>
|
||||
<p className="mt-6 text-base leading-7 text-gray-200">
|
||||
Learning cybersecurity is like becoming the guardian of the digital realm.
|
||||
</TopicCard>
|
||||
<TopicCard title = "Learn C++">
|
||||
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
||||
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||
</p>
|
||||
</div>
|
||||
<div className="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
||||
<div className="rounded-2xl bg-gray-50 py-10 text-center ring-1 ring-inset ring-gray-900/5 lg:flex lg:flex-col lg:justify-center lg:py-16">
|
||||
<div className="mx-auto max-w-xs px-8">
|
||||
<p className="mt-6 flex items-baseline justify-center gap-x-2">
|
||||
<span className="text-5xl font-bold tracking-tight text-gray-900">Start</span>
|
||||
<span className="text-sm font-semibold leading-6 tracking-wide text-gray-900">now!</span>
|
||||
</p>
|
||||
<a
|
||||
href="#"
|
||||
className="mt-10 block w-full rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
||||
>
|
||||
Begin
|
||||
</a>
|
||||
<p className="mt-6 text-xs leading-5 text-gray-600">
|
||||
Take a short quiz to begin learning faster than ever
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TopicCard>
|
||||
*/}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
Loading…
Reference in a new issue