feat: cv verifier
This commit is contained in:
parent
4dd699f08c
commit
194b4b0808
24 changed files with 2199 additions and 70 deletions
51
src/components/verification/DataTable.astro
Normal file
51
src/components/verification/DataTable.astro
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import type { cvTable } from "../../db/schema";
|
||||
|
||||
interface Props {
|
||||
cv: InferSelectModel<typeof cvTable>;
|
||||
}
|
||||
|
||||
const { cv } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="bg-gray-50 dark:bg-gray-700/50 rounded-lg p-6">
|
||||
<ul class="space-y-4 mb-0">
|
||||
<li class="flex items-start">
|
||||
<span class="text-gray-500 dark:text-gray-400 min-w-32"> UUID: </span>
|
||||
<span class="text-gray-800 dark:text-gray-200 font-medium">
|
||||
{cv.uuid}
|
||||
</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<span class="text-gray-500 dark:text-gray-400 min-w-32">
|
||||
Issued by:
|
||||
</span>
|
||||
<span class="text-gray-800 dark:text-gray-200 font-medium">
|
||||
{cv.author}
|
||||
</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<span class="text-gray-500 dark:text-gray-400 min-w-32">
|
||||
Issued to:
|
||||
</span>
|
||||
<span class="text-gray-800 dark:text-gray-200 font-medium">
|
||||
{cv.company_name}
|
||||
</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<span class="text-gray-500 dark:text-gray-400 min-w-32">
|
||||
Issue date:
|
||||
</span>
|
||||
<span class="text-gray-800 dark:text-gray-200 font-medium">
|
||||
{cv.created?.toLocaleString()}
|
||||
</span>
|
||||
</li>
|
||||
<li class="flex items-start">
|
||||
<span class="text-gray-500 dark:text-gray-400 min-w-32"> Purpose: </span>
|
||||
<span class="text-gray-800 dark:text-gray-200 font-medium">
|
||||
{cv.purpose}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
5
src/components/verification/NoCV.astro
Normal file
5
src/components/verification/NoCV.astro
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<div class="text-center p-8 bg-red-50 dark:bg-red-950 rounded-lg shadow-sm">
|
||||
<p class="text-red-600 dark:text-red-400 text-lg font-medium">
|
||||
No CV found with this UUID.
|
||||
</p>
|
||||
</div>
|
||||
20
src/components/verification/Revoked.astro
Normal file
20
src/components/verification/Revoked.astro
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<div class="flex items-center justify-center gap-2 mb-4">
|
||||
<div class="bg-red-100 dark:bg-red-900 p-2 rounded-full">
|
||||
<svg
|
||||
class="w-6 h-6 text-red-600 dark:text-red-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M6 18L18 6M6 6l12 12"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold text-red-600 dark:text-red-400">Revoked CV</h2>
|
||||
</div>
|
||||
<p class="text-red-600 dark:text-red-400 text-lg">
|
||||
This CV has been revoked and is no longer valid.
|
||||
</p>
|
||||
36
src/components/verification/Verified.astro
Normal file
36
src/components/verification/Verified.astro
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
import type { InferSelectModel } from "drizzle-orm";
|
||||
import type { cvTable } from "../../db/schema";
|
||||
|
||||
interface Props {
|
||||
cv: InferSelectModel<typeof cvTable>;
|
||||
}
|
||||
|
||||
const { cv } = Astro.props;
|
||||
---
|
||||
|
||||
<div class="flex items-center gap-2 mb-6">
|
||||
<div class="bg-green-100 dark:bg-green-900 p-2 rounded-full">
|
||||
<svg
|
||||
class="w-6 h-6 text-green-600 dark:text-green-400"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<h2 class="text-2xl font-bold text-gray-800 dark:text-gray-100">
|
||||
Verified CV
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<p class="text-gray-600 dark:text-gray-300 mb-6">
|
||||
This CV was issued and verified by
|
||||
<span class="font-semibold">{cv.author}</span>, for{" "}
|
||||
<span class="font-semibold">{cv.company_name}</span>
|
||||
</p>
|
||||
15
src/db/schema.ts
Normal file
15
src/db/schema.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
|
||||
export const cvTable = sqliteTable("cv", {
|
||||
uuid: text("uuid").primaryKey(),
|
||||
company_name: text("company_name").notNull(),
|
||||
created: integer("created", {
|
||||
mode: "timestamp_ms",
|
||||
}),
|
||||
author: text("author").notNull(),
|
||||
purpose: text("purpose").notNull(),
|
||||
tooling: text("tooling").notNull(),
|
||||
status: text("status", {
|
||||
enum: ["active", "revoked"],
|
||||
}).default("active"),
|
||||
});
|
||||
10
src/env.d.ts
vendored
Normal file
10
src/env.d.ts
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
// src/env.d.ts
|
||||
|
||||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
||||
|
||||
type Runtime = import("@astrojs/cloudflare").Runtime<Env>;
|
||||
|
||||
declare namespace App {
|
||||
interface Locals extends Runtime {}
|
||||
}
|
||||
39
src/pages/cv_verification/index.astro
Normal file
39
src/pages/cv_verification/index.astro
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
import { eq } from "drizzle-orm";
|
||||
import { drizzle } from "drizzle-orm/d1";
|
||||
import { cvTable } from "../../db/schema";
|
||||
import BaseLayout from "../../layouts/BaseLayout.astro";
|
||||
import DataTable from "../../components/verification/DataTable.astro";
|
||||
import NoCV from "../../components/verification/NoCV.astro";
|
||||
import Verified from "../../components/verification/Verified.astro";
|
||||
import Revoked from "../../components/verification/Revoked.astro";
|
||||
|
||||
export const prerender = false;
|
||||
const uuid = Astro.url.searchParams.get("uuid");
|
||||
|
||||
const db = drizzle(Astro.locals.runtime.env.DB);
|
||||
|
||||
const cv = uuid
|
||||
? await db.select().from(cvTable).where(eq(cvTable.uuid, uuid))
|
||||
: [];
|
||||
---
|
||||
|
||||
<BaseLayout title="CV Verification">
|
||||
<section class="max-w-4xl mx-auto min-h-screen">
|
||||
{
|
||||
cv.length === 0 ? (
|
||||
<NoCV />
|
||||
) : cv[0].status !== "active" ? (
|
||||
<div class="text-center p-8 bg-red-50 dark:bg-red-950 rounded-lg shadow-sm">
|
||||
<Revoked />
|
||||
<DataTable cv={cv[0]} />
|
||||
</div>
|
||||
) : (
|
||||
<div class="bg-white dark:bg-gray-800 p-8 rounded-lg shadow-lg">
|
||||
<Verified cv={cv[0]} />
|
||||
<DataTable cv={cv[0]} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</section>
|
||||
</BaseLayout>
|
||||
|
|
@ -4,7 +4,7 @@ import BaseLayout from "../layouts/BaseLayout.astro";
|
|||
---
|
||||
|
||||
<BaseLayout>
|
||||
<h1>Hi, my name is Alex!</h1>
|
||||
<h1 class="text-4xl sm:text-5xl">Hi, my name is Alex!</h1>
|
||||
<p>
|
||||
I am a software engineer, Linux enthusiast and a friend of lightweight,
|
||||
resilient systems.
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@
|
|||
*/
|
||||
|
||||
body {
|
||||
font-family: "Ubuntu";
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
background-size: 100% 600px;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
font-size: 20px;
|
||||
line-height: 1.7;
|
||||
font-family: "Ubuntu";
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: left;
|
||||
background-size: 100% 600px;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
font-size: 20px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
h1,
|
||||
|
|
@ -22,83 +22,83 @@ h3,
|
|||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0.5rem 0 0.5rem 0;
|
||||
line-height: 1.2;
|
||||
margin: 0.5rem 0 0.5rem 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
h1 {
|
||||
font-size: 3.052em;
|
||||
font-size: 3.052em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 2.441em;
|
||||
font-size: 2.441em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.953em;
|
||||
font-size: 1.953em;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1.563em;
|
||||
font-size: 1.563em;
|
||||
}
|
||||
h5 {
|
||||
font-size: 1.25em;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
strong,
|
||||
b {
|
||||
font-weight: 700;
|
||||
font-weight: 700;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0.75em;
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
.prose p {
|
||||
margin-bottom: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
input {
|
||||
font-size: 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
table {
|
||||
width: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 8px;
|
||||
}
|
||||
code {
|
||||
padding: 2px 5px;
|
||||
background-color: rgb(var(--gray-light));
|
||||
border-radius: 2px;
|
||||
padding: 2px 5px;
|
||||
background-color: rgb(var(--gray-light));
|
||||
border-radius: 2px;
|
||||
}
|
||||
pre {
|
||||
padding: 0.5em;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 2em;
|
||||
padding: 0.5em;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
pre > code {
|
||||
all: unset;
|
||||
all: unset;
|
||||
}
|
||||
blockquote {
|
||||
border-left: 4px solid var(--accent);
|
||||
padding: 0 0 0 20px;
|
||||
margin: 0px;
|
||||
font-size: 1.333em;
|
||||
border-left: 4px solid var(--accent);
|
||||
padding: 0 0 0 20px;
|
||||
margin: 0px;
|
||||
font-size: 1.333em;
|
||||
}
|
||||
hr {
|
||||
border: none;
|
||||
border-top: 1px solid rgb(var(--gray-light));
|
||||
border: none;
|
||||
border-top: 1px solid rgb(var(--gray-light));
|
||||
}
|
||||
ul:not(ul ul, ol ul),
|
||||
ol:not(ul ol, ol ol) {
|
||||
margin-bottom: 2em;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.footnotes {
|
||||
margin-left: 2rem;
|
||||
margin-left: 2rem;
|
||||
}
|
||||
.footnotes ol {
|
||||
list-style: outside;
|
||||
list-style: outside;
|
||||
}
|
||||
.footnotes li {
|
||||
margin-top: -3rem;
|
||||
margin-top: -3rem;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue