начал работу над функциональностью Message, мелкие правки

This commit is contained in:
dyakonovr 2024-03-26 21:03:55 +04:00
parent ed3f2cb63e
commit 5476b11fb5
18 changed files with 362 additions and 25 deletions

View File

@ -11,6 +11,8 @@ import SectionCreate from "./components/Section/Create";
import SectionEdit from "./components/Section/Edit"; import SectionEdit from "./components/Section/Edit";
import ThreadsList from "./components/Thread/List"; import ThreadsList from "./components/Thread/List";
import ThreadCreate from "./components/Thread/Create"; import ThreadCreate from "./components/Thread/Create";
import MessagesList from "./components/Message/List";
import MessageCreate from "./components/Message/Create";
function App() { function App() {
return ( return (
@ -39,6 +41,7 @@ function App() {
create={<ThreadCreate />} create={<ThreadCreate />}
// edit={<SectionEdit />} // edit={<SectionEdit />}
/> />
<Resource name="messages" list={<MessagesList />} create={<MessageCreate />} />
</Admin> </Admin>
); );
} }

View File

@ -0,0 +1,26 @@
import {
Create,
ReferenceInput,
SelectInput,
SimpleForm,
TextInput,
required
} from "react-admin";
function MessageCreate() {
return (
<Create>
<SimpleForm>
<TextInput source="text" fullWidth multiline />
<ReferenceInput source="thread_id" reference="threads">
<SelectInput optionText="name" fullWidth validate={required()} />
</ReferenceInput>
<ReferenceInput source="user_id" reference="users">
<SelectInput optionText="nickname" fullWidth validate={required()} />
</ReferenceInput>
</SimpleForm>
</Create>
);
}
export default MessageCreate;

View File

@ -0,0 +1,27 @@
import {
Edit,
ReferenceInput,
SelectInput,
SimpleForm,
TextInput,
required,
} from "react-admin";
import Title from "../Title";
function SectionEdit() {
return (
<Edit title={<Title prefixText="Сообщение" />} actions={false}>
<SimpleForm>
<TextInput source="text" fullWidth multiline />
<ReferenceInput source="thread_id" reference="threads">
<SelectInput optionText="name" fullWidth validate={required()} />
</ReferenceInput>
<ReferenceInput source="user_id" reference="users">
<SelectInput optionText="nickname" fullWidth validate={required()} />
</ReferenceInput>
</SimpleForm>
</Edit>
);
}
export default SectionEdit;

View File

@ -0,0 +1,17 @@
import { List, Datagrid, TextField, EditButton } from "react-admin";
function MessagesList() {
return (
<List>
<Datagrid>
<TextField source="id" />
<TextField source="text" />
<TextField source="user_id" />
<TextField source="thread_id" />
<EditButton />
</Datagrid>
</List>
);
};
export default MessagesList;

View File

@ -12,7 +12,11 @@ function ThreadCreate() {
<Create> <Create>
<SimpleForm> <SimpleForm>
<TextInput source="name" fullWidth /> <TextInput source="name" fullWidth />
<ReferenceInput source="section_id" reference="sections"> <ReferenceInput
source="section_id"
reference="sections"
filter={{ onlyEndSections: true }}
>
<SelectInput optionText="name" fullWidth validate={required()} /> <SelectInput optionText="name" fullWidth validate={required()} />
</ReferenceInput> </ReferenceInput>
<ReferenceInput source="creator_id" reference="users"> <ReferenceInput source="creator_id" reference="users">

View File

@ -0,0 +1,144 @@
import { ApiErrorHandler } from "../../error/api-error.handler";
import Message from "../../models/message.model";
import type { IMessageDto } from "./message.dto";
import type { Order } from "sequelize";
import type { NextFunction, Request, Response } from "express";
import type {
FilterQueryParamType,
RangeQueryParamType,
SortQueryParamType
} from "../query-param.types";
class MessageController {
getAll = async (req: Request, res: Response, next: NextFunction) => {
try {
const range: RangeQueryParamType = req.query.range
? JSON.parse(req.query.range.toString())
: [0, 10];
const sort: SortQueryParamType = req.query.sort
? JSON.parse(req.query.sort.toString())
: ["id", "ASC"];
const Messages = await Message.findAndCountAll({
offset: range[0],
limit: range[1] - range[0] + 1,
order: [sort] as Order
});
return res.json({ data: Messages.rows, total: Messages.count });
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
getMany = async (
req: Request,
res: Response<{ data: Message[] }>,
next: NextFunction
) => {
try {
const filter: FilterQueryParamType = req.query.filter
? JSON.parse(req.query.filter.toString())
: { ids: [] };
const messages = await Message.findAll({
where: { id: filter.ids as number[] }
});
return res.json({ data: messages });
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
create = async (
req: Request<object, object, IMessageDto>,
res: Response<Message>,
next: NextFunction
) => {
try {
const { text, user_id, thread_id } = req.body;
const message = await Message.create({ text, user_id, thread_id });
return res.json(message);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
getById = async (
req: Request<{ id: number }>,
res: Response<Message>,
next: NextFunction
) => {
try {
const { id } = req.params;
const message = await Message.findByPk(id);
if (!message) return next(ApiErrorHandler.notFound("Такой роли не найдено"));
return res.json(message);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
update = async (
req: Request<{ id: number }, object, IMessageDto>,
res: Response<Message>,
next: NextFunction
) => {
try {
const { id } = req.params;
const { text, user_id, thread_id } = req.body;
const message = await Message.findByPk(id);
if (!message) return next(ApiErrorHandler.internal("Такой роли не найдено"));
message.text = text;
message.user_id = user_id;
message.thread_id = thread_id;
await message.save();
return res.json(message);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
delete = async (
req: Request<{ id: number }>,
res: Response<Message>,
next: NextFunction
) => {
try {
const { id } = req.params;
const message = await Message.findByPk(id);
if (!message) return next(ApiErrorHandler.internal("Такой роли не найдено"));
await message.destroy();
return res.json(message);
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
deleteMany = async (
req: Request,
res: Response<{ data: Message[] }>,
next: NextFunction
) => {
try {
const filter: FilterQueryParamType = req.query.filter
? JSON.parse(req.query.filter.toString())
: {};
const messages = await Message.findAll({ where: filter });
Message.destroy({ where: filter });
return res.json({ data: messages });
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
}
export default new MessageController();

View File

@ -0,0 +1,5 @@
export interface IMessageDto {
text: string;
user_id: number;
thread_id: number;
}

View File

@ -16,7 +16,7 @@ class SectionController {
// чтобы не возвращать его в списке // чтобы не возвращать его в списке
const filter: FilterQueryParamType = req.query.filter const filter: FilterQueryParamType = req.query.filter
? JSON.parse(req.query.filter.toString()) ? JSON.parse(req.query.filter.toString())
: { id: -1 }; : {};
const range: RangeQueryParamType = req.query.range const range: RangeQueryParamType = req.query.range
? JSON.parse(req.query.range.toString()) ? JSON.parse(req.query.range.toString())
@ -36,6 +36,24 @@ class SectionController {
} }
}); });
// if (filter.onlyEndSections) {
// console.log('!');
// const endSections = sections.rows.filter(async (section) => {
// const innerSections = await Section.findAll({
// where: {
// root_section_id: section.id
// }
// });
// console.log(section.dataValues.name, innerSections.length);
// return innerSections.length === 0;
// });
// console.log(endSections);
// return res.json({ data: endSections, total: endSections.length });
// }
return res.json({ data: sections.rows, total: sections.count }); return res.json({ data: sections.rows, total: sections.count });
} catch (error) { } catch (error) {
next(ApiErrorHandler.internal((error as Error).message)); next(ApiErrorHandler.internal((error as Error).message));

View File

@ -38,15 +38,20 @@ class UserController {
} }
}; };
// getMany = async (req, res, next) => { getMany = async (req: Request, res: Response<{ data: User[] }>, next: NextFunction) => {
// try { try {
// const filter = JSON.parse(req.query.filter); const filter: FilterQueryParamType = req.query.filter
// const users = await User.findAll({ attributes: { exclude: ["slug"] }, where: { id: filter.ids } }); ? JSON.parse(req.query.filter.toString())
// return res.json({ data: users }); : { ids: [] };
// } catch (error) {
// next(ApiErrorHandler.internal(error.message)); const users = await User.findAll({
// } where: { id: filter.ids as number[] }
// } });
return res.json({ data: users });
} catch (error) {
next(ApiErrorHandler.internal((error as Error).message));
}
};
create = async ( create = async (
req: Request<object, object, IUserDto>, req: Request<object, object, IUserDto>,

View File

@ -4,6 +4,7 @@ import Role from "./models/role.model";
import User from "./models/user.model"; import User from "./models/user.model";
import Section from "./models/section.model"; import Section from "./models/section.model";
import Thread from "./models/thread.model"; import Thread from "./models/thread.model";
import Message from "./models/message.model";
dotenv.config(); dotenv.config();
@ -14,5 +15,5 @@ export const sequelize = new Sequelize({
password: process.env.DB_PASSWORD, password: process.env.DB_PASSWORD,
host: process.env.DB_HOST, host: process.env.DB_HOST,
port: process.env.DB_PORT, port: process.env.DB_PORT,
models: [Role, User, Section, Thread] models: [Role, User, Section, Thread, Message]
}); });

View File

@ -0,0 +1,61 @@
import {
Table,
Column,
Model,
DataType,
Length,
UpdatedAt,
CreatedAt,
ForeignKey,
BelongsTo
} from "sequelize-typescript";
import User from "./user.model";
import Thread from "./thread.model";
@Table({
timestamps: true,
tableName: "messages",
modelName: "Message"
})
export default class Message extends Model {
@Column({
primaryKey: true,
autoIncrement: true,
type: DataType.INTEGER
})
declare id: number;
@Length({ min: 1 })
@Column({
allowNull: false,
defaultValue: "Unknown",
type: DataType.STRING
})
declare text: string;
@CreatedAt
declare created_at: Date;
@UpdatedAt
declare updated_at: Date;
@ForeignKey(() => User)
@Column({
allowNull: false,
type: DataType.INTEGER
})
declare user_id: number;
@BelongsTo(() => User)
declare user: User;
@ForeignKey(() => Thread)
@Column({
allowNull: false,
type: DataType.INTEGER
})
declare thread_id: number;
@BelongsTo(() => Thread)
declare thread: Thread;
}

View File

@ -44,12 +44,12 @@ export default class Section extends Model {
}) })
declare root_section_id: number | null; declare root_section_id: number | null;
@BelongsTo(() => Section, "root_section_id") @BelongsTo(() => Section)
declare root_section: Section; declare root_section: Section;
@HasMany(() => Section, "root_section_id") @HasMany(() => Section)
declare inner_sections: Section[]; declare inner_sections: Section[];
// @HasMany(() => Thread, "section_id") @HasMany(() => Thread)
// declare threads: Thread[]; declare threads: Thread[];
} }

View File

@ -7,10 +7,12 @@ import {
UpdatedAt, UpdatedAt,
CreatedAt, CreatedAt,
ForeignKey, ForeignKey,
BelongsTo BelongsTo,
HasMany
} from "sequelize-typescript"; } from "sequelize-typescript";
import Section from "./section.model"; import Section from "./section.model";
import User from "./user.model"; import User from "./user.model";
import Message from "./message.model";
@Table({ @Table({
tableName: "threads", tableName: "threads",
@ -45,7 +47,7 @@ export default class Thread extends Model {
}) })
declare section_id: number; declare section_id: number;
@BelongsTo(() => Section, "section_id") @BelongsTo(() => Section)
declare section: Section; declare section: Section;
@ForeignKey(() => User) @ForeignKey(() => User)
@ -55,6 +57,9 @@ export default class Thread extends Model {
}) })
declare creator_id: number | null; declare creator_id: number | null;
@BelongsTo(() => User, "creator_id") @BelongsTo(() => User)
declare creator: User; declare creator: User;
@HasMany(() => Message)
declare messages: Message[];
} }

View File

@ -8,10 +8,11 @@ import {
UpdatedAt, UpdatedAt,
ForeignKey, ForeignKey,
BelongsTo, BelongsTo,
HasMany HasMany,
} from "sequelize-typescript"; } from "sequelize-typescript";
import Role from "./role.model"; import Role from "./role.model";
import Thread from "./thread.model"; import Thread from "./thread.model";
import Message from "./message.model";
@Table({ @Table({
timestamps: true, timestamps: true,
@ -69,9 +70,12 @@ export default class User extends Model {
}) })
declare role_id: number; declare role_id: number;
@BelongsTo(() => Role, "role_id") @BelongsTo(() => Role)
declare role: Role; declare role: Role;
// @HasMany(() => Thread, "thread_id") @HasMany(() => Thread)
// declare threads: Thread[]; declare threads: Thread[];
@HasMany(() => Message)
declare messages: Message[];
} }

View File

@ -3,6 +3,7 @@ import roleRouter from "./routes/role.router";
import userRouter from "./routes/user.router"; import userRouter from "./routes/user.router";
import sectionRouter from "./routes/section.router"; import sectionRouter from "./routes/section.router";
import threadRouter from "./routes/thread.router"; import threadRouter from "./routes/thread.router";
import messageRouter from "./routes/message.router";
export const router = Router(); export const router = Router();
@ -10,3 +11,4 @@ router.use("/roles", roleRouter);
router.use("/users", userRouter); router.use("/users", userRouter);
router.use("/sections", sectionRouter); router.use("/sections", sectionRouter);
router.use("/threads", threadRouter); router.use("/threads", threadRouter);
router.use("/messages", messageRouter);

View File

@ -0,0 +1,14 @@
import { Router } from "express";
import MessageController from "../../controllers/message/message.controller";
const messageRouter = Router();
messageRouter.post("/", MessageController.create);
messageRouter.get("/", MessageController.getAll);
messageRouter.get("/get-many", MessageController.getMany);
messageRouter.get("/:id", MessageController.getById);
messageRouter.put("/:id", MessageController.update);
messageRouter.delete("/delete-many", MessageController.deleteMany);
messageRouter.delete("/:id", MessageController.delete);
export default messageRouter;

View File

@ -5,6 +5,7 @@ const userRouter = Router();
userRouter.post("/", UserController.create); userRouter.post("/", UserController.create);
userRouter.get("/", UserController.getAll); userRouter.get("/", UserController.getAll);
userRouter.get("/get-many", UserController.getMany);
userRouter.get("/:id", UserController.getById); userRouter.get("/:id", UserController.getById);
userRouter.put("/:id", UserController.update); userRouter.put("/:id", UserController.update);
userRouter.delete("/delete-many", UserController.deleteMany); userRouter.delete("/delete-many", UserController.deleteMany);

View File

@ -21,7 +21,7 @@
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ "useDefineForClassFields": false, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */ /* Modules */