mirror of
https://github.com/usatiuk/y.git
synced 2025-10-29 02:37:49 +01:00
posts prettier
This commit is contained in:
@@ -1,20 +1,14 @@
|
|||||||
import "./Auth.scss";
|
import "./Auth.scss";
|
||||||
import { Form, Link, useActionData, useNavigation } from "react-router-dom";
|
import { Form, Link, useActionData, useNavigation } from "react-router-dom";
|
||||||
import { loginAction } from "./actions";
|
import { ActionToType, loginAction } from "./actions";
|
||||||
import { isError } from "./api/dto";
|
import { isError } from "./api/dto";
|
||||||
|
|
||||||
export function Login() {
|
export function Login() {
|
||||||
const data = useActionData() as
|
const data = useActionData() as ActionToType<typeof loginAction>;
|
||||||
| Awaited<ReturnType<typeof loginAction>>
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
if (data && !isError(data)) {
|
|
||||||
return <div className="authForm">Login success</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let errors: JSX.Element[] = [];
|
let errors: JSX.Element[] = [];
|
||||||
|
|
||||||
if (data) {
|
if (isError(data)) {
|
||||||
errors = data.errors.map((e) => {
|
errors = data.errors.map((e) => {
|
||||||
return <a>{e}</a>;
|
return <a>{e}</a>;
|
||||||
});
|
});
|
||||||
|
|||||||
19
client/src/Post.scss
Normal file
19
client/src/Post.scss
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.post {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border: 1px solid #E0E0E0;
|
||||||
|
border-radius: 7px;
|
||||||
|
padding: 7px;
|
||||||
|
resize: none;
|
||||||
|
margin: 1rem 0;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
word-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.createdDate {
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: #A0A0A0;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
client/src/Post.tsx
Normal file
16
client/src/Post.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import "./Post.scss";
|
||||||
|
|
||||||
|
export function Post({
|
||||||
|
text,
|
||||||
|
createdDate,
|
||||||
|
}: {
|
||||||
|
text: string;
|
||||||
|
createdDate: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={"post"}>
|
||||||
|
<span className={"text"}>{text}</span>
|
||||||
|
<span className={"createdDate"}>{createdDate}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -55,6 +55,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.posts {
|
.posts {
|
||||||
padding: 2rem;
|
padding: 0 2rem 2rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,7 @@ import { Form, Link, useLoaderData } from "react-router-dom";
|
|||||||
import { LoaderToType, profileSelfLoader } from "./loaders";
|
import { LoaderToType, profileSelfLoader } from "./loaders";
|
||||||
import { isError } from "./api/dto";
|
import { isError } from "./api/dto";
|
||||||
import { ProfileCard } from "./ProfileCard";
|
import { ProfileCard } from "./ProfileCard";
|
||||||
|
import { Post } from "./Post";
|
||||||
|
|
||||||
export interface IProfileProps {
|
export interface IProfileProps {
|
||||||
self: boolean;
|
self: boolean;
|
||||||
@@ -16,6 +17,11 @@ export function Profile(props: IProfileProps) {
|
|||||||
if (!loaderData || isError(loaderData)) {
|
if (!loaderData || isError(loaderData)) {
|
||||||
return <div>Error</div>;
|
return <div>Error</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sortedPosts = loaderData.posts?.sort(
|
||||||
|
(a, b) => b.createdAt - a.createdAt,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={"profileView"}>
|
<div className={"profileView"}>
|
||||||
<div className={"profileInfo"}>
|
<div className={"profileInfo"}>
|
||||||
@@ -29,12 +35,15 @@ export function Profile(props: IProfileProps) {
|
|||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
<div className={"posts"}>
|
<div className={"posts"}>
|
||||||
{loaderData.posts &&
|
{sortedPosts &&
|
||||||
loaderData.posts.map((p) => {
|
sortedPosts.map((p) => {
|
||||||
|
const date = new Date(p.createdAt * 1000);
|
||||||
return (
|
return (
|
||||||
<div key={p.id} className={"post"}>
|
<Post
|
||||||
{p.text}
|
text={p.text}
|
||||||
</div>
|
createdDate={`${date.toUTCString()}`}
|
||||||
|
key={p.id}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,20 +1,14 @@
|
|||||||
import "./Auth.scss";
|
import "./Auth.scss";
|
||||||
import { Form, Link, useActionData, useNavigation } from "react-router-dom";
|
import { Form, Link, useActionData, useNavigation } from "react-router-dom";
|
||||||
import { signupAction } from "./actions";
|
import { ActionToType, signupAction } from "./actions";
|
||||||
import { isError } from "./api/dto";
|
import { isError } from "./api/dto";
|
||||||
|
|
||||||
export function Signup() {
|
export function Signup() {
|
||||||
const data = useActionData() as
|
const data = useActionData() as ActionToType<typeof signupAction>;
|
||||||
| Awaited<ReturnType<typeof signupAction>>
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
if (data && !isError(data)) {
|
|
||||||
return <div className="authForm">Signup success</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let errors: JSX.Element[] = [];
|
let errors: JSX.Element[] = [];
|
||||||
|
|
||||||
if (data) {
|
if (isError(data)) {
|
||||||
errors = data.errors.map((e) => {
|
errors = data.errors.map((e) => {
|
||||||
return <a>{e}</a>;
|
return <a>{e}</a>;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ import { isError } from "./api/dto";
|
|||||||
import { setToken } from "./api/utils";
|
import { setToken } from "./api/utils";
|
||||||
import { post } from "./api/Post";
|
import { post } from "./api/Post";
|
||||||
|
|
||||||
|
export type ActionToType<T extends (...args: any) => any> =
|
||||||
|
| Exclude<Awaited<ReturnType<T>>, Response>
|
||||||
|
| undefined;
|
||||||
|
|
||||||
export async function loginAction({ request }: ActionFunctionArgs) {
|
export async function loginAction({ request }: ActionFunctionArgs) {
|
||||||
const formData = await request.formData();
|
const formData = await request.formData();
|
||||||
const ret = await login(
|
const ret = await login(
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export const PostTo = z.object({
|
|||||||
id: z.number(),
|
id: z.number(),
|
||||||
authorUuid: z.string(),
|
authorUuid: z.string(),
|
||||||
text: z.string(),
|
text: z.string(),
|
||||||
|
createdAt: z.number(),
|
||||||
});
|
});
|
||||||
export type TPostTo = z.infer<typeof PostTo>;
|
export type TPostTo = z.infer<typeof PostTo>;
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@ package com.usatiuk.tjv.y.server.dto;
|
|||||||
|
|
||||||
import com.usatiuk.tjv.y.server.entity.Post;
|
import com.usatiuk.tjv.y.server.entity.Post;
|
||||||
|
|
||||||
public record PostTo(Long id, String authorUuid, String text) {
|
public record PostTo(Long id, String authorUuid, String text, Long createdAt) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
package com.usatiuk.tjv.y.server.dto.converters;
|
package com.usatiuk.tjv.y.server.dto.converters;
|
||||||
|
|
||||||
import com.usatiuk.tjv.y.server.dto.PersonTo;
|
|
||||||
import com.usatiuk.tjv.y.server.dto.PostTo;
|
import com.usatiuk.tjv.y.server.dto.PostTo;
|
||||||
import com.usatiuk.tjv.y.server.entity.Person;
|
|
||||||
import com.usatiuk.tjv.y.server.entity.Post;
|
import com.usatiuk.tjv.y.server.entity.Post;
|
||||||
|
|
||||||
public class PostMapper {
|
public class PostMapper {
|
||||||
public static PostTo makeDto(Post post) {
|
public static PostTo makeDto(Post post) {
|
||||||
return new PostTo(post.getId(), post.getAuthor().getUuid(), post.getText());
|
return new PostTo(post.getId(), post.getAuthor().getUuid(), post.getText(), post.getCreatedAt().getEpochSecond());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
package com.usatiuk.tjv.y.server.entity;
|
package com.usatiuk.tjv.y.server.entity;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.ManyToOne;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
import org.hibernate.annotations.CreationTimestamp;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Getter
|
@Getter
|
||||||
@@ -21,9 +28,12 @@ public class Post implements EntityWithId<Long> {
|
|||||||
@ManyToOne
|
@ManyToOne
|
||||||
private Person author;
|
private Person author;
|
||||||
|
|
||||||
@Lob
|
@NotBlank
|
||||||
private String text;
|
private String text;
|
||||||
|
|
||||||
|
@CreationTimestamp
|
||||||
|
private Instant createdAt;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|||||||
Reference in New Issue
Block a user