diff --git a/app/layout/project.tsx b/app/layout/project.tsx index 947b087..811ec2c 100644 --- a/app/layout/project.tsx +++ b/app/layout/project.tsx @@ -1,18 +1,16 @@ import { BlogModel, ProjectModel, RecordModel } from 'models' import './style/project.scss' import { createStore, produce } from 'solid-js/store' -import { Show, createEffect } from 'solid-js' +import { Component, Show, createEffect } from 'solid-js' import { useNavigate, useParams } from '@solidjs/router' import { fmt_bytes, fmt_datetime, httpx } from 'shared' import { Confact, Editable } from 'comps' -import { ImageIcon, TrashIcon } from 'icons' +import { FileIcon, ImageIcon, TrashIcon } from 'icons' export default () => { type State = { edit_name: boolean project: ProjectModel - blogs: BlogModel[] - records: RecordModel[] } const [state, setState] = createStore({ edit_name: false, @@ -27,8 +25,6 @@ export default () => { updated_at: 0, api_key: null, }, - blogs: [], - records: [], }) const params = useParams() const nav = useNavigate() @@ -52,30 +48,6 @@ export default () => { }) }) - createEffect(() => { - if (!state.project.id) return - - httpx({ - url: `/api/projects/${state.project.id}/blogs/`, - method: 'GET', - params: { page: 0 }, - onLoad(x) { - if (x.status != 200) return - setState({ blogs: x.response }) - }, - }) - - httpx({ - url: `/api/projects/${state.project.id}/records/`, - method: 'GET', - params: { page: 0 }, - onLoad(x) { - if (x.status != 200) return - setState({ records: x.response }) - }, - }) - }) - function project_update(data: Pick) { httpx({ url: `/api/projects/${state.project.id}/`, @@ -108,17 +80,6 @@ export default () => { }) } - function blog_add() { - httpx({ - url: `/api/projects/${state.project.id}/blogs/`, - method: 'POST', - onLoad(x) { - if (x.status != 200) return - nav(`/projects/${state.project.id}/blogs/${x.response.id}`) - }, - }) - } - return (
@@ -177,47 +138,176 @@ export default () => { تاریخ آپدیت: {fmt_datetime(state.project.updated_at)}
-
-
- - -
-
- {state.blogs.slice(0, 3).map(b => ( -
nav('blogs/' + b.id)}> -
- } - > - - -
-
- شناسه: - {b.id} - عنوان: - {b.title || '---'} - نشانه: - {b.slug} - تاریخ شروع: - - {fmt_datetime(state.project.created_at)} - - تاریخ آپدیت: - - {fmt_datetime(state.project.updated_at)} - -
+ + +
+ ) +} +type BlogProps = { + project: ProjectModel +} +const Blogs: Component = P => { + type State = { + blogs: BlogModel[] + } + const [state, setState] = createStore({ + blogs: [], + }) + + const nav = useNavigate() + + createEffect(() => { + if (!P.project.id) return + httpx({ + url: `/api/projects/${P.project.id}/blogs/`, + method: 'GET', + params: { page: 0 }, + show_messages: false, + onLoad(x) { + if (x.status != 200) return + setState({ blogs: x.response }) + }, + }) + }) + + function blog_add() { + httpx({ + url: `/api/projects/${P.project.id}/blogs/`, + method: 'POST', + onLoad(x) { + if (x.status != 200) return + nav(`/projects/${P.project.id}/blogs/${x.response.id}`) + }, + }) + } + + return ( +
+
+ + +
+
+ {state.blogs.slice(0, 3).map(b => ( +
nav('blogs/' + b.id)}> +
+ }> + +
- ))} -
+
+ شناسه: + {b.id} + عنوان: + {b.title || '---'} + {/*نشانه: + {b.slug} + تاریخ آپدیت: + {fmt_datetime(b.updated_at)} + */} + تاریخ شروع: + {fmt_datetime(b.created_at)} +
+
+ ))} +
+
+ ) +} + +type RecordProps = { + project: ProjectModel +} +const Records: Component = P => { + type State = { + records: RecordModel[] + } + const [state, setState] = createStore({ + records: [], + }) + + const nav = useNavigate() + + createEffect(() => { + if (!P.project.id) return + httpx({ + url: `/api/projects/${P.project.id}/records/`, + method: 'GET', + params: { page: 0 }, + show_messages: false, + onLoad(x) { + if (x.status != 200) return + setState({ records: x.response }) + }, + }) + }) + + function record_add() { + let el = document.createElement('input') + el.setAttribute('type', 'file') + el.onchange = () => { + if (!el.files || !el.files[0]) return + + let data = new FormData() + data.set('record', el.files[0]) + + httpx({ + url: `/api/projects/${P.project.id}/records/`, + method: 'POST', + data, + onLoad(x) { + if (x.status != 200) return + setState( + produce(s => { + s.records.unshift(x.response) + }) + ) + }, + }) + } + el.click() + } + + return ( +
+
+ + +
+
+ {state.records.slice(0, 3).map(r => ( +
nav('records/' + r.id)}> +
+ } + > + + +
+
+ شناسه: + {r.id} + حجم: + {fmt_bytes(r.size)} + نوع: + {r.mime} + {/* + تاریخ بارگزاری: + {fmt_datetime(r.created_at)}*/} +
+
+ ))}
) diff --git a/app/layout/style/project.scss b/app/layout/style/project.scss index 303695f..6a35065 100644 --- a/app/layout/style/project.scss +++ b/app/layout/style/project.scss @@ -36,79 +36,159 @@ font-family: var(--en); } } +} + +.project-fnd .blogs { + display: flex; + gap: 0.5rem; + justify-content: space-between; - .blogs { + .actions { + flex-shrink: 0; display: flex; + flex-direction: column; gap: 0.5rem; - justify-content: space-between; - .actions { - flex-shrink: 0; + .add-btn { + --color: var(--green); + } + } + + .blog-list { + display: flex; + overflow: hidden; + overflow-x: auto; + padding-bottom: 1rem; + gap: 1rem; + justify-content: flex-start; + align-items: center; + + .blog { display: flex; flex-direction: column; gap: 0.5rem; + border: 2px solid var(--cl); + background-color: var(--cd); + cursor: pointer; + flex-shrink: 0; + border-radius: var(--radius); + + &:hover { + border-color: var(--ca); + } + + .thumbnail { + height: 100px; + aspect-ratio: 16/9; + overflow: hidden; + display: grid; + place-items: center; + + svg { + width: 40%; + height: 40%; + } + + img { + width: 100%; + height: 100%; + } + } + + .blog-info { + display: grid; + direction: rtl; + grid-template-columns: 1fr 3fr; + padding: 1rem; + gap: 0.5rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: border-color 200ms ease-in-out; - .add-btn { - --color: var(--green); + .n { + direction: ltr; + font-family: var(--en); + } } } + } +} - .blog-list { +.project-fnd .records { + display: flex; + gap: 0.5rem; + justify-content: space-between; + + .actions { + flex-shrink: 0; + display: flex; + flex-direction: column; + gap: 0.5rem; + + .add-btn { + --color: var(--green); + } + } + + .record-list { + display: flex; + overflow: hidden; + overflow-x: auto; + padding-bottom: 1rem; + gap: 1rem; + justify-content: flex-start; + align-items: center; + + .record { display: flex; - overflow: hidden; - overflow-x: auto; - padding-bottom: 1rem; - gap: 1rem; - justify-content: flex-start; - align-items: center; + flex-direction: column; + gap: 0.5rem; + border: 2px solid var(--cl); + background-color: var(--cd); + cursor: pointer; + flex-shrink: 0; + border-radius: var(--radius); - .blog { - display: flex; - flex-direction: column; - gap: 0.5rem; - border: 2px solid var(--cl); - background-color: var(--cd); - cursor: pointer; - flex-shrink: 0; - border-radius: var(--radius); - - &:hover { - border-color: var(--ca); + &:hover { + border-color: var(--ca); + } + + .dpy { + height: 100px; + aspect-ratio: 1; + overflow: hidden; + display: grid; + place-items: center; + + svg { + width: 40%; + height: 40%; } - .thumbnail { - height: 100px; - aspect-ratio: 16/9; + img { overflow: hidden; - display: grid; - place-items: center; - - svg { - width: 40%; - height: 40%; - } - - img { - width: 100%; - height: 100%; - } + width: 100%; + height: 100%; + object-fit: contain; + object-position: center; } + } - .blog-info { - display: grid; - direction: rtl; - grid-template-columns: 1fr 3fr; - padding: 1rem; - gap: 0.5rem; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - transition: border-color 200ms ease-in-out; - - .n { - direction: ltr; - font-family: var(--en); - } + .record-info { + display: grid; + direction: rtl; + grid-template-columns: 1fr 2fr; + padding: 1rem; + gap: 0.5rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + transition: border-color 200ms ease-in-out; + + .n { + direction: ltr; + font-family: var(--en); } } }