Display news, improve news form
This commit is contained in:
parent
e434b89e9e
commit
9564c37899
@ -4,12 +4,13 @@
|
||||
<ow-articles>
|
||||
{% for article in news %}
|
||||
<news-article
|
||||
article-id="{{article.id}}"
|
||||
article-title="{{article.title}}"
|
||||
status="{{article.status.as_str()}}"
|
||||
published-at="{{article.published_at}}"
|
||||
created-at="{{article.created_at}}"
|
||||
>
|
||||
{{article.body}}
|
||||
{{article.body|safe}}
|
||||
</news-article>
|
||||
{% endfor %}
|
||||
</ow-articles>
|
||||
|
16
assets/templates/news.html
Normal file
16
assets/templates/news.html
Normal file
@ -0,0 +1,16 @@
|
||||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<ow-articles>
|
||||
{% for article in news %}
|
||||
<news-article
|
||||
article-id="{{article.id}}"
|
||||
article-title="{{article.title}}"
|
||||
status="{{article.status.as_str()}}"
|
||||
published-at="{{article.published_at}}"
|
||||
created-at="{{article.created_at}}"
|
||||
>
|
||||
{{article.body|safe}}
|
||||
</news-article>
|
||||
{% endfor %}
|
||||
</ow-articles>
|
||||
{% endblock %}
|
@ -1,3 +1,3 @@
|
||||
import "./admin/ow-admin";
|
||||
import "./admin/ow-articles";
|
||||
import "./admin/news-article";
|
||||
|
||||
import "./admin/article-form";
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Component, FORM_STYLE } from "../shared";
|
||||
import "../shared/rich-text-editor";
|
||||
|
||||
customElements.define('article-form', class extends Component {
|
||||
constructor() {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Component } from "../shared";
|
||||
import "./ow-articles";
|
||||
|
||||
customElements.define('ow-admin', class extends Component {
|
||||
constructor() {
|
||||
|
@ -8,6 +8,9 @@ import "./price/price-view";
|
||||
import "./price/price-input";
|
||||
import "./register-form.js";
|
||||
import "./business-items";
|
||||
import "./news/ow-articles";
|
||||
import "./news/news-article";
|
||||
import "./shared/rich-text-editor";
|
||||
|
||||
import { fireFbReady } from "./shared.js";
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { Component } from "../shared";
|
||||
import "../shared/date-time";
|
||||
|
||||
customElements.define('news-article', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ["article-title", "status", "body", "created-at", "published-at"]
|
||||
return ["article-id", "article-title", "status", "body", "created-at", "published-at"]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -42,11 +43,13 @@ customElements.define('news-article', class extends Component {
|
||||
<section id="time">
|
||||
<div class="time">
|
||||
<span>Created at:</span>
|
||||
<span id="created_at"></span>
|
||||
<date-time id="created_at" hide-date="false" hide-time="false">
|
||||
</date-time>
|
||||
</div>
|
||||
<div class="time">
|
||||
<span>Published at:</span>
|
||||
<span id="published_at"></span>
|
||||
<date-time id="published_at" hide-date="false" hide-time="false">
|
||||
</date-time>
|
||||
</div>
|
||||
</section>
|
||||
<section id="body">
|
||||
@ -56,6 +59,15 @@ customElements.define('news-article', class extends Component {
|
||||
`);
|
||||
}
|
||||
|
||||
get article_id() {
|
||||
const id = parseInt(this.getAttribute('article-id'));
|
||||
return isNaN(id) ? null : id;
|
||||
}
|
||||
|
||||
set article_id(v) {
|
||||
this.setAttribute('article-id', v);
|
||||
}
|
||||
|
||||
get article_title() {
|
||||
return this.getAttribute('article-title');
|
||||
}
|
||||
@ -79,8 +91,7 @@ customElements.define('news-article', class extends Component {
|
||||
}
|
||||
|
||||
set created_at(v) {
|
||||
this.setAttribute('created-at', v);
|
||||
this.shadowRoot.querySelector('#created_at').textContent = v;
|
||||
this.shadowRoot.querySelector('#created_at').datetime = v;
|
||||
}
|
||||
|
||||
get published_at() {
|
||||
@ -88,7 +99,6 @@ customElements.define('news-article', class extends Component {
|
||||
}
|
||||
|
||||
set published_at(v) {
|
||||
this.setAttribute('published-at', v);
|
||||
this.shadowRoot.querySelector('#published_at').textContent = v;
|
||||
this.shadowRoot.querySelector('#published_at').datetime = v;
|
||||
}
|
||||
});
|
@ -1,5 +1,4 @@
|
||||
import { Component } from "../shared"
|
||||
import "./article-form";
|
||||
|
||||
customElements.define('ow-articles', class extends Component {
|
||||
constructor() {
|
@ -1,16 +0,0 @@
|
||||
import { Component } from "./shared";
|
||||
|
||||
import "./ow-news/news-article";
|
||||
|
||||
customElements.define("ow-news", class extends Component {
|
||||
constructor() {
|
||||
super(`
|
||||
<style>
|
||||
:host { display: block; }
|
||||
</style>
|
||||
<article>
|
||||
<slot></slot>
|
||||
</article>
|
||||
`);
|
||||
}
|
||||
});
|
@ -1,37 +0,0 @@
|
||||
import { Component } from "../shared";
|
||||
|
||||
customElements.define('news-article', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ["title", "body", "published-at"]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(`
|
||||
<style>
|
||||
:host { display: block; }
|
||||
</style>
|
||||
<article>
|
||||
<h1 id="title"></h1>
|
||||
<section id="body"></section>
|
||||
</article>
|
||||
`);
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.getAttribute('title');
|
||||
}
|
||||
|
||||
set title(v) {
|
||||
this.setAttribute('title', v);
|
||||
this.shadowRoot.querySelector('#title').textContent = v;
|
||||
}
|
||||
|
||||
get body() {
|
||||
return this.getAttribute('body');
|
||||
}
|
||||
|
||||
set body(v) {
|
||||
this.setAttribute('body', v);
|
||||
this.shadowRoot.querySelector('#body').textContent = v;
|
||||
}
|
||||
});
|
76
client/src/shared/date-time.js
Normal file
76
client/src/shared/date-time.js
Normal file
@ -0,0 +1,76 @@
|
||||
import { Component } from "../shared";
|
||||
|
||||
customElements.define('date-time', class extends Component {
|
||||
static get observedAttributes() {
|
||||
return ['datetime', "hide-date", "hide-time"]
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super(`
|
||||
<style>
|
||||
:host { display: block; }
|
||||
section { display: flex; justify-content: flex-start; }
|
||||
:host([hide-time = "true"]) #time { display: none; }
|
||||
:host([hide-time = "false"]) #time { margin-left: 8px; }
|
||||
:host([hide-date = "true"]) #date { display: none; }
|
||||
</style>
|
||||
<section>
|
||||
<div id="date"></div>
|
||||
<div id="time"></div>
|
||||
</section>
|
||||
`);
|
||||
}
|
||||
|
||||
get datetime() {
|
||||
return Date.parse(this.getAttribute('datetime'));
|
||||
}
|
||||
|
||||
set datetime(v) {
|
||||
this.setAttribute('datetime', v);
|
||||
this.#format();
|
||||
}
|
||||
|
||||
get hide_date() {
|
||||
return this.getAttribute('hide-date') === 'true';
|
||||
}
|
||||
|
||||
set hide_date(v) {
|
||||
this.setAttribute('hide-date', v === true || v === 'true' ? 'true' : 'false');
|
||||
this.#format();
|
||||
}
|
||||
|
||||
get hide_time() {
|
||||
return this.getAttribute('hide-time') === 'true';
|
||||
}
|
||||
|
||||
set hide_time(v) {
|
||||
this.setAttribute('hide-time', v === true || v === 'true' ? 'true' : 'false');
|
||||
this.#format();
|
||||
}
|
||||
|
||||
#format() {
|
||||
{
|
||||
const el = this.shadowRoot.querySelector('#date');
|
||||
if (!this.hide_date)
|
||||
el.textContent = new Date().toLocaleDateString(navigator.language || navigator.userLanguage, {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric"
|
||||
});
|
||||
else
|
||||
el.textContent = '';
|
||||
}
|
||||
{
|
||||
const el = this.shadowRoot.querySelector('#time');
|
||||
if (!this.hide_date)
|
||||
el.textContent = new Date().toLocaleDateString(navigator.language || navigator.userLanguage, {
|
||||
hour: "numeric",
|
||||
minute: "numeric",
|
||||
date: "short"
|
||||
}).split(' ')[1];
|
||||
else
|
||||
el.textContent = '';
|
||||
}
|
||||
}
|
||||
});
|
@ -216,6 +216,7 @@ customElements.define('rich-text-editor', class extends Component {
|
||||
const img = new Image();
|
||||
img.src = reader.result || '';
|
||||
el.appendChild(img);
|
||||
this.#emitChange();
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
|
@ -126,7 +126,7 @@ struct AccountTemplate {
|
||||
async fn account_page(id: Identity, db: Data<sqlx::PgPool>) -> Result<HttpResponse> {
|
||||
let pool = db.into_inner();
|
||||
let mut t = crate::ok_or_internal!(pool.begin().await);
|
||||
let record = match id.identity() {
|
||||
let account = match id.identity() {
|
||||
Some(id) => queries::account_by_id(&mut t, id).await,
|
||||
_ => {
|
||||
id.forget();
|
||||
@ -136,7 +136,7 @@ async fn account_page(id: Identity, db: Data<sqlx::PgPool>) -> Result<HttpRespon
|
||||
t.commit().await.ok();
|
||||
Ok(HttpResponse::Ok().body(
|
||||
AccountTemplate {
|
||||
account: record,
|
||||
account,
|
||||
error: None,
|
||||
page: Page::Account,
|
||||
}
|
||||
@ -547,6 +547,39 @@ async fn upload(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "news.html")]
|
||||
pub struct NewsTemplate {
|
||||
account: Option<db::Account>,
|
||||
error: Option<String>,
|
||||
page: Page,
|
||||
news: Vec<db::NewsArticle>,
|
||||
}
|
||||
|
||||
#[get("/news")]
|
||||
async fn news(id: Identity, db: Data<PgPool>) -> Result<HttpResponse> {
|
||||
let pool = db.into_inner();
|
||||
let mut t = crate::ok_or_internal!(pool.begin().await);
|
||||
let account = match id.identity() {
|
||||
Some(id) => queries::account_by_id(&mut t, id).await,
|
||||
_ => {
|
||||
id.forget();
|
||||
None
|
||||
}
|
||||
};
|
||||
let news = queries::published_news(&mut t).await.unwrap_or_default();
|
||||
Ok(HttpResponse::Ok().content_type("text/html").body(
|
||||
NewsTemplate {
|
||||
account,
|
||||
error: None,
|
||||
page: Page::News,
|
||||
news,
|
||||
}
|
||||
.render()
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn configure(config: &mut ServiceConfig) {
|
||||
std::fs::create_dir_all("./uploads").expect("Failed to create ./uploads directory");
|
||||
|
||||
@ -562,6 +595,7 @@ pub fn configure(config: &mut ServiceConfig) {
|
||||
)
|
||||
.service(index)
|
||||
.service(account_page)
|
||||
.service(news)
|
||||
.service(register)
|
||||
.service(logout)
|
||||
.service(login)
|
||||
|
Loading…
Reference in New Issue
Block a user