-
Notifications
You must be signed in to change notification settings - Fork 0
feat: open source main app #2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,3 +4,5 @@ dist/ | |
|
|
||
| .astro/ | ||
| .wrangler/ | ||
|
|
||
| .env | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| import cloudflare from "@astrojs/cloudflare"; | ||
| import solid from "@astrojs/solid-js"; | ||
| import tailwindcss from "@tailwindcss/vite"; | ||
| import { defineConfig, envField } from "astro/config"; | ||
|
|
||
| export default defineConfig({ | ||
| output: "server", | ||
| adapter: cloudflare(), | ||
| integrations: [solid()], | ||
| vite: { plugins: [tailwindcss()] }, | ||
| site: "https://icbe.rman.dev", | ||
|
|
||
| env: { | ||
| schema: { | ||
| GITLAB_ACCESS_TOKEN: envField.string({ | ||
| context: "server", | ||
| access: "secret", | ||
| startsWith: "glpat", | ||
| length: 26, | ||
| }), | ||
| GITLAB_PROJECT_ID: envField.number({ | ||
| context: "server", | ||
| access: "secret", | ||
| int: true, | ||
| gt: 1, | ||
| }), | ||
| }, | ||
| }, | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| _worker.js | ||
| _routes.json |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| import { | ||
| submitDisprovenElements, | ||
| submitProvenElements, | ||
| } from "./submit-elements"; | ||
|
|
||
| export const server = { | ||
| elements: { submitDisprovenElements, submitProvenElements }, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| import { ActionError, defineAction } from "astro:actions"; | ||
| import * as z from "astro/zod"; | ||
| import { DataProvenSchema } from "#schemas"; | ||
| import { saveToCloud } from "#utils/gitlab"; | ||
| import { getDisprovenToSave, getProvenToSave } from "#utils/submit-elements"; | ||
|
|
||
| const submitDisprovenElements = defineAction({ | ||
| input: z.object({ elements: z.array(z.object({ name: z.string() })) }), | ||
| async handler(input, context) { | ||
| const role = null; | ||
|
|
||
| if (!role) { | ||
| throw new ActionError({ | ||
| code: "UNAUTHORIZED", | ||
| message: "Please login to use this feature.", | ||
| }); | ||
| } | ||
|
|
||
| const canSubmitElements = false; | ||
|
|
||
| if (!canSubmitElements) { | ||
| throw new ActionError({ | ||
| code: "FORBIDDEN", | ||
| message: "You don't have permissions to use this feature.", | ||
| }); | ||
| } | ||
|
|
||
| const elements = input.elements.map((element) => element.name); | ||
|
|
||
| const data = await getDisprovenToSave(elements); | ||
| await saveToCloud(data); | ||
|
|
||
| return { success: true } as const; | ||
| }, | ||
| }); | ||
|
|
||
| const submitProvenElements = defineAction({ | ||
| input: z.object({ elements: DataProvenSchema }), | ||
| handler: async (input, context) => { | ||
| const role = null; | ||
|
|
||
| if (!role) { | ||
| throw new ActionError({ | ||
| code: "UNAUTHORIZED", | ||
| message: "Please login to use this feature.", | ||
| }); | ||
| } | ||
|
|
||
| const canSubmitElements = false; | ||
|
|
||
| if (!canSubmitElements) { | ||
| throw new ActionError({ | ||
| code: "FORBIDDEN", | ||
| message: "You don't have permissions to use this feature.", | ||
| }); | ||
| } | ||
|
|
||
| const data = await getProvenToSave(input.elements); | ||
| await saveToCloud(data); | ||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| return { success: true } as const; | ||
| }, | ||
| }); | ||
|
|
||
| export { submitDisprovenElements, submitProvenElements }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import type { JSX } from "solid-js"; | ||
|
|
||
| export default function AdminDashboard(): JSX.Element { | ||
| return ( | ||
| <div class="p-6 min-h-screen flex flex-col"> | ||
| <h2 class="text-2xl font-bold mb-6 text-neutral-800 dark:text-neutral-100"> | ||
| Admin Controls | ||
| </h2> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import { type JSX, Show } from "solid-js"; | ||
|
|
||
| import SubmitDisprovenElementsSection from "./dashboard/SubmitDisprovenElementsSection"; | ||
| import SubmitProvenElementsSection from "./dashboard/SubmitProvenElementsSection"; | ||
| import Divider from "./ui/Divider"; | ||
| import Link from "./ui/Link"; | ||
|
|
||
| const TopBar = (): JSX.Element => ( | ||
| <div class="flex items-center justify-between pb-4"> | ||
| <h1 class="text-2xl font-bold text-neutral-800 dark:text-neutral-100"> | ||
| User Dashboard | ||
| </h1> | ||
| </div> | ||
| ); | ||
|
|
||
| export default function UserDashboard(props: { | ||
| canSubmitElements: boolean; | ||
| }): JSX.Element { | ||
| const isAdmin = false; | ||
|
|
||
| return ( | ||
| <div class="p-6 min-h-screen flex flex-col"> | ||
| <TopBar /> | ||
| <Show when={isAdmin}> | ||
| <Divider /> | ||
| <Link | ||
| href="/admin/dashboard" | ||
| color="blue" | ||
| label="Open Admin Dashboard" | ||
| /> | ||
| </Show> | ||
| <Show when={props.canSubmitElements}> | ||
| <Divider /> | ||
| <SubmitDisprovenElementsSection /> | ||
| <Divider /> | ||
| <SubmitProvenElementsSection /> | ||
| </Show> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,97 @@ | ||||||||||||||
| import { actions } from "astro:actions"; | ||||||||||||||
| import type { JSX } from "solid-js"; | ||||||||||||||
| import { createSignal, Show } from "solid-js"; | ||||||||||||||
| import Button from "#components/ui/Button"; | ||||||||||||||
|
|
||||||||||||||
| type SubmitState = | ||||||||||||||
| | { status: "not-started" } | ||||||||||||||
| | { status: "submitting" } | ||||||||||||||
| | { status: "complete"; success: boolean } | ||||||||||||||
| | { status: "error"; error?: string }; | ||||||||||||||
|
|
||||||||||||||
| const SubmitDisprovenElementsResultContainer = (props: { | ||||||||||||||
| state: SubmitState; | ||||||||||||||
| }) => ( | ||||||||||||||
| <Show when={props.state.status === "complete" && props.state} keyed> | ||||||||||||||
| {(state) => ( | ||||||||||||||
| <p | ||||||||||||||
| class={`mt-4 p-4 rounded-md ${ | ||||||||||||||
| state.success | ||||||||||||||
| ? "bg-green-50 dark:bg-green-950" | ||||||||||||||
| : "bg-red-50 dark:bg-red-950" | ||||||||||||||
| }`} | ||||||||||||||
| > | ||||||||||||||
| {state.success | ||||||||||||||
| ? "Elements submitted successfully!" | ||||||||||||||
| : "Elements submission failed."} | ||||||||||||||
| </p> | ||||||||||||||
| )} | ||||||||||||||
| </Show> | ||||||||||||||
|
Comment on lines
+12
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Surface action failures in the result container. Line 15 only renders feedback for Also applies to: 49-57 🤖 Prompt for AI Agents |
||||||||||||||
| ); | ||||||||||||||
|
|
||||||||||||||
| function SubmitDisprovenElementsSection(): JSX.Element { | ||||||||||||||
| const [elementsToBeSubmitted, setElementsToBeSubmitted] = createSignal< | ||||||||||||||
| string[] | ||||||||||||||
| >([]); | ||||||||||||||
| const [submitState, setSubmitState] = createSignal<SubmitState>({ | ||||||||||||||
| status: "not-started", | ||||||||||||||
| }); | ||||||||||||||
|
|
||||||||||||||
| const handleSubmit = async () => { | ||||||||||||||
| try { | ||||||||||||||
| setSubmitState({ status: "submitting" }); | ||||||||||||||
| const disprovenElements = elementsToBeSubmitted().map((name) => ({ | ||||||||||||||
| name, | ||||||||||||||
| })); | ||||||||||||||
| const result = await actions.elements.submitDisprovenElements({ | ||||||||||||||
| elements: disprovenElements, | ||||||||||||||
| }); | ||||||||||||||
| if (result.error) { | ||||||||||||||
| console.error("Submit failed:", result.error); | ||||||||||||||
| setSubmitState({ status: "error", error: result.error.message }); | ||||||||||||||
| } else { | ||||||||||||||
| setSubmitState({ status: "complete", success: result.data.success }); | ||||||||||||||
| } | ||||||||||||||
| } catch (err) { | ||||||||||||||
| console.error("Submit error:", err); | ||||||||||||||
| setSubmitState({ status: "error" }); | ||||||||||||||
| } | ||||||||||||||
| }; | ||||||||||||||
|
|
||||||||||||||
| return ( | ||||||||||||||
| <div class="flex flex-col gap-4"> | ||||||||||||||
| <textarea | ||||||||||||||
| class="text-white p-1 min-h-64" | ||||||||||||||
| placeholder="Enter disproven elements, one per line" | ||||||||||||||
|
Comment on lines
+63
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Textarea content will be unreadable on the default background. Line 64 forces white text without setting a matching dark background. On the native light Suggested fix- class="text-white p-1 min-h-64"
+ class="min-h-64 p-2 border rounded-md bg-white text-black dark:bg-gray-800 dark:text-white dark:border-gray-700"📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
| data-1p-ignore | ||||||||||||||
| onInput={(e) => | ||||||||||||||
| setElementsToBeSubmitted( | ||||||||||||||
| e.currentTarget.value | ||||||||||||||
| .split("\n") | ||||||||||||||
| .map((s) => s.trim()) | ||||||||||||||
| .filter(Boolean), | ||||||||||||||
| ) | ||||||||||||||
| } | ||||||||||||||
| /> | ||||||||||||||
|
|
||||||||||||||
| <Button | ||||||||||||||
| onClick={handleSubmit} | ||||||||||||||
| disabled={ | ||||||||||||||
| submitState().status === "submitting" || | ||||||||||||||
| elementsToBeSubmitted().length === 0 | ||||||||||||||
| } | ||||||||||||||
| label={ | ||||||||||||||
| submitState().status === "submitting" | ||||||||||||||
| ? "Submitting..." | ||||||||||||||
| : elementsToBeSubmitted().length | ||||||||||||||
| ? `Submit disproven Elements (${elementsToBeSubmitted().length})` | ||||||||||||||
| : "Submit disproven Elements" | ||||||||||||||
| } | ||||||||||||||
| /> | ||||||||||||||
|
|
||||||||||||||
| <SubmitDisprovenElementsResultContainer state={submitState()} /> | ||||||||||||||
| </div> | ||||||||||||||
| ); | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| export default SubmitDisprovenElementsSection; | ||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.