Added a TopicCard component (#6)
This commit is contained in:
parent
fda1fae418
commit
4a98209021
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 { CheckIcon } from '@heroicons/react/20/solid'
|
||||||
|
import TopicCard from '../components/topic_card'
|
||||||
|
|
||||||
const includedFeatures = [
|
const includedFeatures = [
|
||||||
'Private forum access',
|
'Private forum access',
|
||||||
|
@ -8,136 +9,38 @@ const includedFeatures = [
|
||||||
]
|
]
|
||||||
|
|
||||||
export default function Example() {
|
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 (
|
return (
|
||||||
|
|
||||||
<div className="bg-black py-24 sm:py-32">
|
<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 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 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">
|
{ cards.map((elem) => {
|
||||||
<svg
|
return (<TopicCard title={elem[0]} link={elem[2]}>{elem[1]}</TopicCard>)
|
||||||
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"
|
<TopicCard title = "Learn cybersecurity">
|
||||||
>
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
<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.
|
|
||||||
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
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.
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
</p>
|
</TopicCard>
|
||||||
</div>
|
<TopicCard title = "Learn geography">
|
||||||
<div className="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
<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>earn cybersecurity
|
|
||||||
<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.
|
|
||||||
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
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.
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
</p>
|
</TopicCard>
|
||||||
</div>
|
<TopicCard title = "Learn C++">
|
||||||
<div className="-mt-2 p-2 lg:mt-0 lg:w-full lg:max-w-md lg:flex-shrink-0">
|
Learning cybersecurity is like becoming a guardian of the digital realm.
|
||||||
<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>earn cybersecurity
|
|
||||||
<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.
|
|
||||||
It's about mastering the art of protecting information, systems, and networks from cyber threats.
|
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.
|
From understanding encryption algorithms to detecting malware, every lesson equips you with tools to fortify against cyber-attacks.
|
||||||
</p>
|
</TopicCard>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in a new issue