Skip to content

Commit

Permalink
feat: show groups on home screen
Browse files Browse the repository at this point in the history
  • Loading branch information
soulsam480 committed Dec 1, 2024
1 parent ef83fd7 commit 1fb6d2b
Show file tree
Hide file tree
Showing 20 changed files with 294 additions and 38 deletions.
6 changes: 3 additions & 3 deletions .mise.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[tools]
erlang = "27.1.2"
rebar = "3.24.0"
gleam = "1.5.1"
gleam = "latest"
erlang = "latest"
rebar = "latest"
Binary file modified bun.lockb
Binary file not shown.
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
{
"name": "okane",
"type": "module",
"devDependencies": {
"daisyui": "^4.12.14",
"@preact/signals-core": "^1.8.0",
"@types/bun": "latest",
"@types/toastify-js": "^1.12.3",
"daisyui": "^4.12.14",
"tailwindcss": "^3.4.14"
},
"peerDependencies": {
"typescript": "^5.0.0"
},
"type": "module",
"dependencies": {
"tinydate": "^1.3.0",
"toastify-js": "^1.12.0"
}
}
14 changes: 14 additions & 0 deletions priv/ui/js/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
*
* @param {string|URL|globalThis.Request} url
* @param {RequestInit} init
*/
export async function $fetch(url, { headers = {}, ...rest } = {}) {
return fetch(url, {
...rest,
headers: {
"Content-Type": "application/json",
...headers,
},
});
}
5 changes: 5 additions & 0 deletions priv/ui/js/components/group_create_form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { html } from "htm";

export function GroupCreateForm() {
return html` <form>some</form> `;
}
47 changes: 47 additions & 0 deletions priv/ui/js/components/groups_list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { html } from "htm";
import { $fetch } from "../client.js";
import { $groups, set_partial } from "../store.js";
import tinydate from "tinydate";

async function load_groups() {
set_partial($groups, { state: "loading" });

const resp = await $fetch("/auth/groups");

if (resp.status === 200) {
const body = await resp.json();

$groups.value = {
state: "idle",
data: body.data,
};
}
}

load_groups();

export function GroupsList() {
return html`
${$groups.value.state === "loading"
? html`<span>Loading groups</span>`
: html`<div class="flex flex-col gap-3">
<div>Groups you're part of</div>
<ul class="flex flex-col gap-2">
${$groups.value.data.map((group) => {
return html`<li
key=${group.id}
class="rounded flex flex-col gap-0.5 px-1.5 py-1 shadow-sm bg-base-200"
>
<div class="text-sm">${group.id}: ${group.name}</div>
<div class="text-xs">
created:
${tinydate("{MMMM} {DD} {YYYY}", {
MMMM: (d) => d.toLocaleString("default", { month: "long" }),
})(new Date(group.created_at))}
</div>
</li>`;
})}
</ul>
</div>`}
`;
}
4 changes: 4 additions & 0 deletions priv/ui/js/components/toasts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import "toastify-js/src/toastify.css";
import toastify from "toastify-js/src/toastify-es.js";

export { toastify };
Empty file added priv/ui/js/pages/groups.js
Empty file.
15 changes: 14 additions & 1 deletion priv/ui/js/pages/home.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
import { $auth_state } from "../store.js";
import { html } from "htm";
import { GroupsList } from "../components/groups_list.js";
import { GroupCreateForm } from "../components/group_create_form.js";

export function home() {
return html`<div>Hello ${$auth_state.value.name}! Welcome to Okane.</div>`;
return html`<div class="flex flex-col gap-4">
<div
class="pb-2 flex justify-between gap-2 items-center border-b border-secondary"
>
<div>Okane</div>
<div>${$auth_state.value.name}</div>
</div>
<${GroupsList} />
<${GroupCreateForm} />
</div>`;
}
7 changes: 7 additions & 0 deletions priv/ui/js/pages/login_form.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { html } from "htm";
import toastify from "toastify-js";

export function login_form() {
/**
Expand All @@ -17,6 +18,12 @@ export function login_form() {

if (response.ok) {
window.location.reload();
} else {
toastify({
text: "Something went wrong!",
gravity: "top",
className: "app-toast app-toast--error",
}).showToast();
}
}

Expand Down
41 changes: 33 additions & 8 deletions priv/ui/js/store.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import { signal, effect } from "htm";

function get_route_from_loc() {
const hash = window.location.hash.replace(/^#/, "");

if (hash.length === 0) {
return null;
}

return hash;
}

/**
* @type {import('htm').Signal<string|null>}
*/
export const $current_route = signal(null);
export const $current_route = signal(get_route_from_loc());

window.addEventListener("hashchange", () => {
const hash = window.location.hash.replace(/^#/, "");

if (!$auth_state.peek()) {
$current_route.value = "login";
return;
}

if (hash.length === 0) {
$current_route.value = null;
} else {
$current_route.value = hash;
}
$current_route.value = get_route_from_loc();
});

export function goto(path) {
Expand All @@ -29,10 +33,31 @@ export function goto(path) {
*/
export const $auth_state = signal(null);

/**
* @type {import('htm').Signal<import('types/store.d.ts').IResourceState<Array<import('types/models.d.ts').Group>>>}
*/
export const $groups = signal({
state: "idle",
data: [],
});

effect(() => {
if ($auth_state.value) {
goto("home");
} else {
goto("login");
}
});

/**
* @param {import('htm').Signal<any>} signal
* @param {any} value
*/
export function set_partial(signal, value) {
const old_val = signal.peek();

signal.value = {
...old_val,
...value,
};
}
60 changes: 55 additions & 5 deletions src/app/controllers/groups.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import gleam/http
import gleam/int
import gleam/list
import gleam/result
import gleam/string
import wisp

fn fetch_create_group_params(
Expand All @@ -32,11 +33,14 @@ fn handle_create_group(req: wisp.Request, ctx: config.Context) -> wisp.Response
case group.insert_group(create_group_params, ctx.db) {
Ok(group) -> wisp.ok() |> wisp.json_body(group_serializer.run(group))

Error(_) ->
Error(error) -> {
wisp.log_error(string.inspect(error))

wisp.internal_server_error()
|> wisp.json_body(base_serializer.serialize_error(
"Unable to create group!",
))
}
}
}

Expand Down Expand Up @@ -73,6 +77,48 @@ fn handle_show_group(group_id: String, ctx: config.Context) -> wisp.Response {
}
}

fn handle_update_group(
group_id: Int,
req: wisp.Request,
ctx: config.Context,
) -> wisp.Response {
use form <- wisp.require_form(req)
use update_group_params <- fetch_create_group_params(form, ctx)

case
group.update_by_id(
group_id,
group.UpdateableGroup(update_group_params.name),
ctx.db,
)
{
Ok(group) -> wisp.ok() |> wisp.json_body(group_serializer.run(group))

Error(error) -> {
wisp.log_error(string.inspect(error))

wisp.internal_server_error()
|> wisp.json_body(base_serializer.serialize_error(
"Unable to update group!",
))
}
}
}

fn handle_delete_group(group_id: Int, ctx: config.Context) {
case group.discard_by_id(group_id, ctx.db) {
Ok(_) -> wisp.no_content()
Error(error) -> {
wisp.log_error(string.inspect(error))

wisp.internal_server_error()
|> wisp.json_body(base_serializer.serialize_error(
"Unable to delete group!",
))
}
}
}

pub fn controller(req: wisp.Request, ctx: config.Context) -> wisp.Response {
case ctx.scoped_segments, req.method {
[], http.Post -> handle_create_group(req, ctx)
Expand All @@ -81,11 +127,15 @@ pub fn controller(req: wisp.Request, ctx: config.Context) -> wisp.Response {

[group_id], http.Get -> handle_show_group(group_id, ctx)

[group_id], http.Put -> {
wisp.not_found()
}
[group_id], http.Put ->
handle_update_group(int.parse(group_id) |> result.unwrap(0), req, ctx)

[group_id], http.Delete ->
handle_delete_group(int.parse(group_id) |> result.unwrap(0), ctx)

[group_id], http.Delete -> wisp.method_not_allowed([http.Delete])
// TODO: implement this next
[group_id, "invite"], http.Post -> wisp.method_not_allowed([http.Post])
[group_id, "remove"], http.Post -> wisp.method_not_allowed([http.Post])

_, __ -> wisp.not_found()
}
Expand Down
8 changes: 8 additions & 0 deletions src/app/css/app.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

.app-toast {
@apply alert p-1;
}

.app-toast--error {
@apply alert-error;
}
10 changes: 9 additions & 1 deletion src/app/db/connection.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,13 @@ pub fn run_query_with(
})
|> tap_debug("with params:: ")

sql |> sqlight.query(on: db_conn, with: db_params, expecting: dcdr)
// TODO: remove this later
let results =
sql
|> sqlight.query(on: db_conn, with: db_params, expecting: dcdr)
|> tap_debug("returned:: ")

io.println("---- query end ----")

results
}
Loading

0 comments on commit 1fb6d2b

Please sign in to comment.