This commit is contained in:
Mootfrost777 2023-01-04 00:30:08 +03:00
parent cc306f33ef
commit e33dba1b45
12 changed files with 172 additions and 47 deletions

View file

@ -57,7 +57,7 @@ def run_migrations_offline() -> None:
def do_run_migrations(connection: Connection) -> None: def do_run_migrations(connection: Connection) -> None:
context.configure(connection=connection, target_metadata=target_metadata) context.configure(connection=connection, target_metadata=target_metadata, compare_server_default=True, compare_type=True)
with context.begin_transaction(): with context.begin_transaction():
context.run_migrations() context.run_migrations()

View file

@ -0,0 +1,28 @@
"""test
Revision ID: 9b7ccd38c01a
Revises: dfb99ba1c75e
Create Date: 2023-01-03 23:30:42.286979
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '9b7ccd38c01a'
down_revision = 'dfb99ba1c75e'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View file

@ -0,0 +1,28 @@
"""test
Revision ID: b16d4a12f55f
Revises: 9b7ccd38c01a
Create Date: 2023-01-03 23:33:23.628323
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'b16d4a12f55f'
down_revision = '9b7ccd38c01a'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View file

@ -0,0 +1,34 @@
"""set server default
Revision ID: d4125430c46f
Revises: e0a3988a875d
Create Date: 2023-01-04 00:16:43.307461
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'd4125430c46f'
down_revision = 'e0a3988a875d'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('notes', 'checked',
existing_type=sa.BOOLEAN(),
server_default='f',
existing_nullable=False)
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.alter_column('notes', 'checked',
existing_type=sa.BOOLEAN(),
server_default=None,
existing_nullable=False)
# ### end Alembic commands ###

View file

@ -0,0 +1,28 @@
"""test
Revision ID: e0a3988a875d
Revises: b16d4a12f55f
Create Date: 2023-01-03 23:43:28.541936
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e0a3988a875d'
down_revision = 'b16d4a12f55f'
branch_labels = None
depends_on = None
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###
def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
pass
# ### end Alembic commands ###

View file

@ -20,7 +20,7 @@ class Note(Base):
id = sa.Column(sa.Integer, primary_key=True) id = sa.Column(sa.Integer, primary_key=True)
title = sa.Column(sa.String, nullable=False) title = sa.Column(sa.String, nullable=False)
checked = sa.Column(sa.Boolean, nullable=False, default=False) checked = sa.Column(sa.Boolean, nullable=False, server_default='f')
__all__ = ['engine', 'Base', 'async_session', 'Note'] __all__ = ['engine', 'Base', 'async_session', 'Note']

View file

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends from fastapi import APIRouter, Depends, Response
from pydantic import BaseModel from pydantic import BaseModel
from sqlalchemy import select, update, delete from sqlalchemy import select, update, delete
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@ -16,20 +16,23 @@ async def get_notes(session: AsyncSession = Depends(get_session)):
return result.scalars().all() return result.scalars().all()
class NoteResponse(BaseModel):
id: int
title: str
checked: bool
class NoteCreate(BaseModel): class NoteCreate(BaseModel):
title: str title: str
@router.post("/items/create") @router.post('/items/create', response_model=NoteResponse)
async def create_note(req: NoteCreate, session: AsyncSession = Depends(get_session)): async def create_note(req: NoteCreate, session: AsyncSession = Depends(get_session)):
note = Note(title=req.title) note = Note(title=req.title)
session.add(note) session.add(note)
await session.commit() await session.commit()
return { await session.refresh(note)
"id": note.id, return NoteResponse(id=note.id, title=note.title, checked=note.checked)
"title": note.title,
"checked": note.checked
}
class NoteUpdateReq(BaseModel): class NoteUpdateReq(BaseModel):
@ -38,30 +41,26 @@ class NoteUpdateReq(BaseModel):
checked: bool checked: bool
@router.put("/items/update") @router.put('/items/update', response_model=NoteResponse)
async def update_note(req: NoteUpdateReq, session: AsyncSession = Depends(get_session)): async def update_note(req: NoteUpdateReq, session: AsyncSession = Depends(get_session)):
await session.execute( await session.execute(
update(Note).where(Note.id == req.id).values(req.__dict__) update(Note).where(Note.id == req.id).values(req.dict())
) )
await session.commit() await session.commit()
return { return NoteResponse(**req.dict())
"id": req.id,
"title": req.title,
"checked": req.checked
}
class NoteDeleteReq(BaseModel): class NoteDeleteReq(BaseModel):
id: int id: int
@router.delete("/items/delete") @router.delete('/items/delete')
async def delete_note(req: NoteDeleteReq, session: AsyncSession = Depends(get_session)): async def delete_note(req: NoteDeleteReq, session: AsyncSession = Depends(get_session)):
await session.execute( await session.execute(
delete(Note).where(Note.id == req.id) delete(Note).where(Note.id == req.id)
) )
await session.commit() await session.commit()
return 204 return Response(None, 204)
__all__ = ["router"] __all__ = ["router"]

View file

@ -4,42 +4,38 @@ import Item from "./components/Item.vue";
import Edit from "./components/Edit.vue"; import Edit from "./components/Edit.vue";
import config from "./config"; import config from "./config";
import axios from 'axios' import axios from 'axios'
import INote from "./INote";
onMounted(async () => { onMounted(async () => {
const response = await axios.get(`${config.apiEndpoint}/items`) const response = await axios.get<INote[]>(`${config.apiEndpoint}/items`)
notes.value = response.data notes.value = response.data
id = Math.max(...notes.value.map((note: any) => note.id), 0)
currentNote.value.id = id += 1
}) })
const notes = ref<INote[]>([])
const editingNote = ref<INote>({id: -1, title: "", checked: false})
let id; async function updateNote(note: INote) {
const notes = ref<any[]>([])
const currentNote = ref<any>({id: id += 1, title: "", checked: false})
async function processNote(note: any) {
const index = notes.value.findIndex((item) => item.id === note.id) const index = notes.value.findIndex((item) => item.id === note.id)
if (index !== -1) { if (index !== -1) {
await axios.put(`${config.apiEndpoint}/items/update`, note).then((response) => { const resp = await axios.put(`${config.apiEndpoint}/items/update`, note)
notes.value[index] = response.data notes.value[index] = resp.data
})
} }
else { else {
await axios.post(`${config.apiEndpoint}/items/create`, {title: note.title}).then((response) => { const resp = await axios.post(`${config.apiEndpoint}/items/create`, {title: note.title})
notes.value.push(response.data) notes.value.push(resp.data)
})
} }
currentNote.value = {id: id += 1, title: "", checked: false} editingNote.value = {id: -1, title: "", checked: false}
} }
async function deleteNote(note: any) { async function deleteNote(note: INote) {
await axios.delete(`${config.apiEndpoint}/items/delete`, {data: {id: note.id}}) await axios.delete(`${config.apiEndpoint}/items/delete`, {data: {id: note.id}})
notes.value = notes.value.filter((item) => item.id !== note.id) notes.value = notes.value.filter((item) => item.id !== note.id)
} }
async function updateChecked(id: any, checked: boolean) { async function updateChecked(id: number, checked: boolean) {
const index = notes.value.findIndex((item) => item.id === id) const index = notes.value.findIndex((item) => item.id === id)
if (index !== -1) { if (index !== -1) {
notes.value[index].checked = checked notes.value[index].checked = checked
@ -53,15 +49,15 @@ async function updateChecked(id: any, checked: boolean) {
<item v-for="el in notes" <item v-for="el in notes"
:note="el" :note="el"
:key="el.id" :key="el.id"
@delete="deleteNote" @delete="() => deleteNote(el)"
@edit="(note) => currentNote = note" @edit="() => editingNote = {...el}"
@updateChecked="(checked) => updateChecked(el.id, checked)" @updateChecked="(checked) => updateChecked(el.id, checked)"
/> />
</div> </div>
<edit <edit
v-bind:note="currentNote" :note="editingNote"
@done="processNote" @done="updateNote"
@updateTitle="(title) => currentNote.title = title" @updateTitle="(title) => editingNote.title = title"
/> />
</template> </template>

5
frontend/src/INote.ts Normal file
View file

@ -0,0 +1,5 @@
export default interface INote {
id: number
title: string
checked: boolean
}

View file

@ -1,7 +1,9 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ note: any }>() import INote from '../INote'
defineProps<{ note: INote }>()
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'done', note: any): void, (e: 'done', note: INote): void,
(e: 'updateTitle', title: string): void (e: 'updateTitle', title: string): void
}>() }>()

View file

@ -1,8 +1,10 @@
<script setup lang="ts"> <script setup lang="ts">
defineProps<{ note: any }>() import INote from '../INote'
defineProps<{ note: INote }>()
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'delete', note: any): void (e: 'delete'): void
(e: 'edit', note: any): void (e: 'edit'): void
(e: 'updateChecked', checked: boolean): void (e: 'updateChecked', checked: boolean): void
}>() }>()
@ -15,8 +17,8 @@
<div class="container"> <div class="container">
<input type="checkbox" :checked="note.checked" @change="handleChecked"> <input type="checkbox" :checked="note.checked" @change="handleChecked">
<span class="title">{{ note.title }}</span> <span class="title">{{ note.title }}</span>
<div class="btn" @click="$emit('edit', note)"><span><font-awesome-icon icon="fa-solid fa-pen-to-square" /> Edit</span></div> <div class="btn" @click="$emit('edit')"><span><font-awesome-icon icon="fa-solid fa-pen-to-square" /> Edit</span></div>
<div class="btn" @click="$emit('delete', note)"><span><font-awesome-icon icon="fa-solid fa-trash" /> Delete</span></div> <div class="btn" @click="$emit('delete')"><span><font-awesome-icon icon="fa-solid fa-trash" /> Delete</span></div>
</div> </div>
</template> </template>

View file

@ -4,4 +4,7 @@ declare module '*.vue' {
import type { DefineComponent } from 'vue' import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any> const component: DefineComponent<{}, {}, any>
export default component export default component
export interface Note {
}
} }