[FWA-5] Update Home List Function
This commit is contained in:
parent
b938f296c1
commit
ac841ba8e0
@ -12,6 +12,7 @@ from backend.core.config import get_app_settings
|
||||
from backend.db.db_setup import session_context
|
||||
from backend.repos.repository_users import RepositoryUsers
|
||||
from backend.repos.seeder import default_users_init
|
||||
from backend.repos.seeder.init_house import default_house_init
|
||||
|
||||
PROJECT_DIR = Path(__file__).parent.parent.parent
|
||||
|
||||
@ -20,6 +21,7 @@ logger = root_logger.get_logger()
|
||||
def init_db(db) -> None:
|
||||
logger.info("Initializing user data...")
|
||||
default_users_init(db)
|
||||
default_house_init(db)
|
||||
|
||||
def db_is_at_head(alembic_cfg: config.Config) -> bool:
|
||||
settings = get_app_settings()
|
||||
|
@ -9,13 +9,13 @@ class RepositoryHouses:
|
||||
self.areas = Areas()
|
||||
|
||||
def get_all(self, skip: int = 0, limit: int = 100):
|
||||
return self.houses.query.filter_by(deleted_at=None).offset(skip).limit(limit).all()
|
||||
return self.houses.query.filter_by(deleted_at=None).offset(skip*limit).limit(limit).all()
|
||||
|
||||
def get_all_areas(self, house_id: str, skip: int = 0, limit: int = 100):
|
||||
return self.areas.query.filter_by(deleted_at=None, house_id=house_id).offset(skip).limit(limit).all()
|
||||
|
||||
def get_count_all(self, skip: int = 0, limit: int = 100):
|
||||
return self.houses.query.filter_by(deleted_at=None).offset(skip).limit(limit).count()
|
||||
def get_count_all(self):
|
||||
return self.houses.query.filter_by(deleted_at=None).count()
|
||||
|
||||
def get_by_id(self, house_id: str):
|
||||
return self.houses.query.filter_by(id=house_id).one()
|
||||
|
@ -1 +1,2 @@
|
||||
from .init_users import default_users_init
|
||||
from .init_house import default_house_init
|
||||
|
26
backend/repos/seeder/init_house.py
Normal file
26
backend/repos/seeder/init_house.py
Normal file
@ -0,0 +1,26 @@
|
||||
from backend.core.config import get_app_settings
|
||||
from backend.core.root_logger import get_logger
|
||||
from backend.repos.repository_houses import RepositoryHouses
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from backend.schemas.house.house import HouseCreate
|
||||
|
||||
|
||||
logger = get_logger("init_house")
|
||||
settings = get_app_settings()
|
||||
|
||||
def dev_houses() -> list[dict]:
|
||||
list = []
|
||||
for x in range(20):
|
||||
list.append({
|
||||
"icon": "IconAccessible",
|
||||
"name": f"Home{x+1}",
|
||||
"address": f"Address{x+1}",
|
||||
"areas": [{"name": f"Area{x+1}", "desc": "Description"}],
|
||||
})
|
||||
return list
|
||||
|
||||
def default_house_init(session: Session):
|
||||
houses = RepositoryHouses()
|
||||
for house in dev_houses():
|
||||
houses.create(session, HouseCreate(**house))
|
@ -12,19 +12,13 @@ settings = get_app_settings()
|
||||
def dev_users() -> list[dict]:
|
||||
return [
|
||||
{
|
||||
"username": "sam",
|
||||
"username": "admin",
|
||||
"password": "admin",
|
||||
"name": "Sam",
|
||||
"is_admin": True,
|
||||
},
|
||||
{
|
||||
"username": "duy",
|
||||
"password": "admin",
|
||||
"name": "Duy",
|
||||
"is_admin": True,
|
||||
},
|
||||
{
|
||||
"username": "sam1",
|
||||
"username": "admin1",
|
||||
"password": "admin",
|
||||
"name": "Sam1",
|
||||
"is_admin": False,
|
||||
|
@ -28,6 +28,6 @@ def create_house(house: HouseCreate, db: db_dependency, current_user: current_us
|
||||
|
||||
@public_router.get("/all", response_model=ReturnValue[HousesListResponse])
|
||||
async def get_all_house(page: int, pageSize: int, current_user: current_user_token) -> ReturnValue[HousesListResponse]:
|
||||
housesCount = house_service.get_all_count(skip=page-1, limit=pageSize)
|
||||
housesCount = house_service.get_all_count()
|
||||
houses = house_service.get_all(skip=page-1, limit=pageSize)
|
||||
return ReturnValue(status=200, data={'total': housesCount, 'list': list(houses)})
|
||||
|
@ -14,4 +14,4 @@ class HouseService(BaseService):
|
||||
return self.repos.get_all(skip=skip, limit=limit)
|
||||
|
||||
def get_all_count(self, skip: int = 0, limit: int = 100):
|
||||
return self.repos.get_count_all(skip=skip, limit=limit)
|
||||
return self.repos.get_count_all()
|
||||
|
@ -8,8 +8,8 @@ export const postCreateHouse = (payload) => {
|
||||
export const getAllHouse = ({ page, pageSize }) => {
|
||||
return protocol.get(
|
||||
GET_HOUSES_LIST({
|
||||
page: page(),
|
||||
pageSize: pageSize(),
|
||||
page,
|
||||
pageSize,
|
||||
}),
|
||||
{},
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import ConfirmPopup from '@components/common/ConfirmPopup'
|
||||
import Popup from '@components/common/Popup'
|
||||
import TextInput from '@components/common/TextInput'
|
||||
import Textarea from '@components/common/Textarea'
|
||||
@ -11,7 +12,7 @@ import {
|
||||
IconInfoCircle,
|
||||
IconVector,
|
||||
} from '@tabler/icons-solidjs'
|
||||
import { For, Show, createSignal } from 'solid-js'
|
||||
import { For, Show, createComponent, createSignal } from 'solid-js'
|
||||
import * as yup from 'yup'
|
||||
import AreaItem from './AreaItem'
|
||||
|
||||
@ -48,10 +49,19 @@ export default function AreaAdd(props) {
|
||||
setOpenModal(true)
|
||||
}
|
||||
|
||||
const onDeleteAreaItem = (index) => {
|
||||
const onConfirmDelete = (index) => {
|
||||
setData((prev) => [...prev.slice(0, index), ...prev.slice(index + 1)])
|
||||
}
|
||||
|
||||
const onClickDeleteItem = (index) => {
|
||||
createComponent(ConfirmPopup, {
|
||||
title: language?.message['CONFIRM_DELETE'],
|
||||
children: language?.message['CONFIRM_DELETE_NOTE'],
|
||||
deleteId: index,
|
||||
onConfirm: onConfirmDelete,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="form-control mb-3">
|
||||
<div class="join join-vertical">
|
||||
@ -93,7 +103,7 @@ export default function AreaAdd(props) {
|
||||
{...item}
|
||||
formName={props.name}
|
||||
key={index()}
|
||||
onDelete={onDeleteAreaItem}
|
||||
onDelete={onClickDeleteItem}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
|
@ -1,21 +1,44 @@
|
||||
import { IconCirclePlus } from '@tabler/icons-solidjs'
|
||||
import useLanguage from '@hooks/useLanguage'
|
||||
import { IconAlertTriangle } from '@tabler/icons-solidjs'
|
||||
import { Show, createSignal } from 'solid-js'
|
||||
import Popup from '../Popup'
|
||||
|
||||
export default function ConfirmPopup(props) {
|
||||
const [openModal, setOpenModal] = createSignal(true)
|
||||
const { language } = useLanguage()
|
||||
|
||||
const onConfirm = () => {
|
||||
props.onConfirm(props.deleteId)
|
||||
setOpenModal(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<Show when={openModal()}>
|
||||
<Popup
|
||||
icon={<IconCirclePlus size={20} class="text-green-500" />}
|
||||
title="abc"
|
||||
titleClass="text-lg"
|
||||
icon={<IconAlertTriangle size={20} class="text-red-500" />}
|
||||
title={props.title}
|
||||
titleClass="text-md"
|
||||
openModal={openModal()}
|
||||
onModalClose={() => setOpenModal(false)}
|
||||
class="!w-4/12 !max-w-4xl"
|
||||
>
|
||||
<div class="modal-body">{props.children}</div>
|
||||
<div class="modal-action">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary btn-sm"
|
||||
onClick={onConfirm}
|
||||
>
|
||||
{language.ui.confirm}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-ghost btn-sm"
|
||||
onClick={() => setOpenModal(false)}
|
||||
>
|
||||
{language.ui.cancel}
|
||||
</button>
|
||||
</div>
|
||||
</Popup>
|
||||
</Show>
|
||||
)
|
||||
|
@ -23,6 +23,7 @@ export default function Pagination(props) {
|
||||
<div class="pagination join">
|
||||
<button
|
||||
class="join-item btn btn-sm border border-gray-300"
|
||||
disabled={localProps.currentPage() === 1}
|
||||
onClick={onPrevious}
|
||||
>
|
||||
«
|
||||
@ -54,6 +55,10 @@ export default function Pagination(props) {
|
||||
</For>
|
||||
<button
|
||||
class="join-item btn btn-sm border border-gray-300"
|
||||
disabled={
|
||||
localProps.currentPage() ===
|
||||
Math.ceil(localProps.totalCount() / localProps.pageSize())
|
||||
}
|
||||
onClick={onNext}
|
||||
>
|
||||
»
|
||||
|
@ -14,7 +14,7 @@ export const usePagination = ({
|
||||
siblingCount = 1,
|
||||
}) => {
|
||||
const paginationRange = createMemo(() => {
|
||||
const totalPageCount = Math.ceil(totalCount / pageSize())
|
||||
const totalPageCount = Math.ceil(totalCount() / pageSize())
|
||||
|
||||
const totalPageNumbers = siblingCount + 5
|
||||
|
||||
|
@ -1,4 +1,64 @@
|
||||
{
|
||||
"login": "Login",
|
||||
"logout": "Logout"
|
||||
"ui": {
|
||||
"username": "User name",
|
||||
"password": "Password",
|
||||
"login": "Login",
|
||||
"logout": "Logout",
|
||||
"dashboard": "Dashboard",
|
||||
"profile": "Profile",
|
||||
"changeInfo": "Account information",
|
||||
"save": "Save",
|
||||
"clear": "Clear",
|
||||
"house": "Location",
|
||||
"action": "Action",
|
||||
"createNew": "Create new",
|
||||
"location": "Warehouse",
|
||||
"displayName": "Display name",
|
||||
"newPassword": "New password",
|
||||
"confirmNewPassword": "Confirm new password",
|
||||
"newHouse": "Create new location",
|
||||
"houseName": "Location name",
|
||||
"houseIcon": "Icon",
|
||||
"houseAddress": "Address",
|
||||
"areas": "Areas",
|
||||
"areaName": "Area name",
|
||||
"areaDesc": "Description",
|
||||
"addArea": "Add area",
|
||||
"create": "Create",
|
||||
"update": "Update",
|
||||
"delete": "Delete",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"findIconHere": "Find icons here",
|
||||
"empty": "Empty",
|
||||
"error": "Error!",
|
||||
"success": "Success!",
|
||||
"info": "Info",
|
||||
"loading": "Loading...",
|
||||
"showing": "Showing"
|
||||
},
|
||||
"table": {
|
||||
"columnName": {
|
||||
"no": "No.",
|
||||
"name": "Name",
|
||||
"icon": "Icon",
|
||||
"address": "Address",
|
||||
"action": "Action"
|
||||
}
|
||||
},
|
||||
"message": {
|
||||
"CREATE_USER_SUCCESS": "Create account successfully!",
|
||||
"CREATED_USER": "Username is already in use!",
|
||||
"LOGIN_WRONG": "Wrong username or password.",
|
||||
"USER_LOCK": "Your account is locked.",
|
||||
"IS_REQUIRED": "%s is required.",
|
||||
"PASSWORD_MUSTMATCH": "Password must match.",
|
||||
"UPDATE_SUCCESS": "Update successfully!",
|
||||
"UPDATE_PROFILE_SUCCESS": "Update profile successfully!",
|
||||
"UPDATE_FAIL": "Update failed!",
|
||||
"API_CALL_FAIL": "API call failed!",
|
||||
"CREATE_HOUSE_FAIL": "Create new location failed!",
|
||||
"CREATE_HOUSE_SUCCESS": "Create new location successfully!",
|
||||
"CONFIRM_DELETE": "Are you sure you want to delete this item?"
|
||||
}
|
||||
}
|
||||
|
@ -58,6 +58,8 @@
|
||||
"UPDATE_FAIL": "Cập nhật thất bại!",
|
||||
"API_CALL_FAIL": "Call API có vẫn đề!",
|
||||
"CREATE_HOUSE_FAIL": "Tạo địa điểm mới thất bại!",
|
||||
"CREATE_HOUSE_SUCCESS": "Tạo địa điểm mới thành công!"
|
||||
"CREATE_HOUSE_SUCCESS": "Tạo địa điểm mới thành công!",
|
||||
"CONFIRM_DELETE": "Bạn có chắc là muốn xóa mục này?",
|
||||
"CONFIRM_DELETE_NOTE": "Chú Ý ‼: Một khi đã XÓA thì không thể nào khôi phục lại được."
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,29 @@
|
||||
import ViewSwitch, { VIEWDATA } from '@components/ViewSwitch'
|
||||
import useLanguage from '@hooks/useLanguage'
|
||||
import { IconHome } from '@tabler/icons-solidjs'
|
||||
import * as icons from '@tabler/icons-solidjs'
|
||||
import { For, createEffect, createResource, createSignal } from 'solid-js'
|
||||
|
||||
import { getAllHouse } from '@api/house'
|
||||
import Pagination from '@components/common/Pagination'
|
||||
import { A } from '@solidjs/router'
|
||||
import {
|
||||
IconHome2,
|
||||
IconHomeDot,
|
||||
IconHome,
|
||||
IconPencil,
|
||||
IconSquareRoundedPlus,
|
||||
IconTrash,
|
||||
} from '@tabler/icons-solidjs'
|
||||
import { Dynamic } from 'solid-js/web'
|
||||
import './house.scss'
|
||||
|
||||
const PAGE_SIZE = [10, 50, 100]
|
||||
|
||||
const getPaginationText = (total, size, page) => {
|
||||
const start = (page - 1) * size + 1
|
||||
const end = Math.min(start + size - 1, total)
|
||||
|
||||
return `${start} - ${end} / ${total}`
|
||||
}
|
||||
|
||||
const fetchHouses = async ({ page, pageSize }) => {
|
||||
const response = await getAllHouse({ page, pageSize })
|
||||
return response
|
||||
@ -27,14 +34,17 @@ export default function House() {
|
||||
const [pageSize, setPageSize] = createSignal(PAGE_SIZE[0])
|
||||
const [currentPage, setCurrentPage] = createSignal(1)
|
||||
const [view, setView] = createSignal(VIEWDATA['list'])
|
||||
const [record, setRecord] = createSignal(null)
|
||||
const [totalRecord, setTotalRecord] = createSignal(0)
|
||||
const [houses] = createResource(
|
||||
{ page: currentPage, pageSize: pageSize },
|
||||
() => ({ page: currentPage(), pageSize: pageSize() }),
|
||||
fetchHouses,
|
||||
)
|
||||
|
||||
createEffect(() => {
|
||||
if (houses()) {
|
||||
console.log(houses()?.data)
|
||||
setRecord(houses()?.data?.list)
|
||||
setTotalRecord(houses()?.data?.total)
|
||||
}
|
||||
})
|
||||
|
||||
@ -84,56 +94,43 @@ export default function House() {
|
||||
<div class="col w-4/12">{language.table.columnName.address}</div>
|
||||
<div class="col w-2/12">{language.table.columnName.action}</div>
|
||||
</div>
|
||||
<div class="row view-item">
|
||||
<div class="col hide w-1/12">1</div>
|
||||
<div class="col w-1/12">
|
||||
<IconHome2 size={21} />
|
||||
</div>
|
||||
<div class="col w-4/12">Nhà 1</div>
|
||||
<div class="col w-4/12">Data 4</div>
|
||||
<div class="col actionbar w-2/12">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm px-1 text-blue-500 mr-2"
|
||||
onClick={onEdit}
|
||||
>
|
||||
<IconPencil size={20} />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm px-1 text-red-500"
|
||||
onClick={onDelete}
|
||||
>
|
||||
<IconTrash size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row view-item">
|
||||
<div class="col hide w-1/12">2</div>
|
||||
<div class="col w-1/12">
|
||||
<IconHomeDot size={21} />
|
||||
</div>
|
||||
<div class="col w-4/12">Nhà 2</div>
|
||||
<div class="col w-4/12">Data 4</div>
|
||||
<div class="col actionbar w-2/12">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm px-1 text-blue-500 mr-2"
|
||||
onClick={onEdit}
|
||||
>
|
||||
<IconPencil size={20} />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm px-1 text-red-500"
|
||||
onClick={onDelete}
|
||||
>
|
||||
<IconTrash size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<For each={record()}>
|
||||
{(item, idx) => (
|
||||
<div class="row view-item">
|
||||
<div class="col hide w-1/12">
|
||||
{pageSize() * currentPage() - (pageSize() - idx() - 1)}
|
||||
</div>
|
||||
<div class="col w-1/12">
|
||||
<Dynamic component={icons[item?.icon]} size={21} />
|
||||
</div>
|
||||
<div class="col w-4/12">{item?.name}</div>
|
||||
<div class="col w-4/12">{item?.address}</div>
|
||||
<div class="col actionbar w-2/12">
|
||||
<button
|
||||
class="btn btn-ghost btn-sm px-1 text-blue-500 mr-2"
|
||||
onClick={onEdit}
|
||||
>
|
||||
<IconPencil size={20} />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-ghost btn-sm px-1 text-red-500"
|
||||
onClick={onDelete}
|
||||
>
|
||||
<IconTrash size={20} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-botbar flex max-xs:flex-col justify-between items-center gap-2 mt-5">
|
||||
<div class="bar-left flex gap-2 justify-start items-center">
|
||||
<div class="py-2 px-3 border rounded-lg border-gray-300 leading-none">
|
||||
<span>{language.ui.showing}: 1 - 10 / 100</span>
|
||||
<span>
|
||||
{language.ui.showing}:{' '}
|
||||
{getPaginationText(totalRecord(), pageSize(), currentPage())}
|
||||
</span>
|
||||
</div>
|
||||
<div class="dropdown dropdown-top dropdown-end">
|
||||
<div
|
||||
@ -160,7 +157,7 @@ export default function House() {
|
||||
<div class="bar-right">
|
||||
<Pagination
|
||||
currentPage={currentPage}
|
||||
totalCount={1000}
|
||||
totalCount={totalRecord}
|
||||
pageSize={pageSize}
|
||||
onPageChange={setCurrentPage}
|
||||
/>
|
||||
|
Loading…
x
Reference in New Issue
Block a user