Skip to content

Commit

Permalink
등록된 정보 관리 페이지 추가 (#2)
Browse files Browse the repository at this point in the history
* 등록된 정보 관리 페이지 추가

* 건물 정보가 없으면 버튼 이름을 변경해주기

* 테이블 형태로 바꾸기 & 정보 더 노출하기
  • Loading branch information
Zeniuus authored Apr 6, 2024
1 parent 1ac9066 commit 7df5ccc
Show file tree
Hide file tree
Showing 10 changed files with 354 additions and 0 deletions.
36 changes: 36 additions & 0 deletions app/(private)/accessibility/components/AccessibilityRow.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { styled } from "@/styles/jsx"

export const AccessibilityRow = styled("tr")

export const Cell = styled("td", {
base: {
margin: "8px 0",
padding: "8px 4px",
textAlign: "center",
lineHeight: 0,
border: "1px black solid",
},
})

export const Image = styled("img", {
base: {
width: 200,
margin: "4px auto"
},
})

export const DeleteButton = styled("button", {
base: {
display: "block",
padding: "16px 8px",
margin: "4px auto",
backgroundColor: "var(--leaf-primary-60)",
color: "white",
borderRadius: 4,
cursor: "pointer",
_disabled: {
backgroundColor: "var(--leaf-grey-80)",
cursor: "not-allowed",
},
},
})
61 changes: 61 additions & 0 deletions app/(private)/accessibility/components/AccessibilityRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"use client"

import React from "react"
import {AccessibilitySummary, BuildingAccessibility, PlaceAccessibility} from "@/lib/models/accessibility";
import * as S from "@/(private)/accessibility/components/AccessibilityRow.style";
import { format } from "date-fns";
import {useModal} from "@/hooks/useModal";

interface Props {
accessibilitySummary: AccessibilitySummary
onDeletePlaceAccessibility: (accessibilitySummary: AccessibilitySummary) => void
onDeleteBuildingAccessibility: (accessibilitySummary: AccessibilitySummary) => void
}
export default function AccessibilityRow(props: Props) {
const {
accessibilitySummary: accessibilitySummary,
onDeletePlaceAccessibility,
onDeleteBuildingAccessibility,
} = props
const { openModal } = useModal()

const onClickImage = (imageUrl: string) => {
openModal({ type: "AccessibilityImage", props: { imageUrl } })
}

return (
<S.AccessibilityRow key={accessibilitySummary.placeAccessibility.id}>
<S.Cell>{accessibilitySummary.placeAccessibility.placeName}</S.Cell>
<S.Cell>{accessibilitySummary.placeAccessibility.registeredUserName || "익명의 정복자"}</S.Cell>
<S.Cell>{format(accessibilitySummary.placeAccessibility.createdAtMillis, "yyyy-MM-dd HH:mm:ss")}</S.Cell>
<S.Cell>
{
accessibilitySummary.placeAccessibility.imageUrls.map((imageUrl) => (
<S.Image src={imageUrl} onClick={() => onClickImage(imageUrl)} />
))
}
</S.Cell>
<S.Cell>
{
accessibilitySummary.buildingAccessibility?.imageUrls.map((imageUrl) => (
<S.Image src={imageUrl} />
))
}
</S.Cell>
<S.Cell>
<S.DeleteButton onClick={() => onDeletePlaceAccessibility(accessibilitySummary)}>
장소 정보 삭제
</S.DeleteButton>
{
accessibilitySummary.buildingAccessibility
? (
<S.DeleteButton onClick={() => onDeleteBuildingAccessibility(accessibilitySummary)}>
건물 정보 삭제
</S.DeleteButton>
)
: <S.DeleteButton disabled>건물 정보 없음</S.DeleteButton>
}
</S.Cell>
</S.AccessibilityRow>
)
}
70 changes: 70 additions & 0 deletions app/(private)/accessibility/page.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { styled } from "@/styles/jsx"

export const Page = styled("main", {
base: {
padding: 32,
},
})

export const PageTitle = styled("h1", {
base: {
fontSize: 28,
fontWeight: "bold",
marginBottom: 32,
},
})

export const SearchButton = styled("button", {
base: {
padding: "8px 16px",
backgroundColor: "var(--leaf-primary-60)",
color: "white",
borderRadius: 4,
cursor: "pointer",
_disabled: {
backgroundColor: "var(--leaf-grey-80)",
cursor: "not-allowed",
},
},
})

export const LoadNextPageButton = styled("button", {
base: {
padding: 8,
backgroundColor: "var(--leaf-primary-60)",
color: "white",
borderRadius: 4,
cursor: "pointer",
_disabled: {
backgroundColor: "var(--leaf-grey-80)",
cursor: "not-allowed",
},
},
})

export const TableWrapper = styled("div", {
base: {
padding: "20px 20px",
},
})
export const AccessibilityTable = styled("table", {
base: {
tableLayout: "auto",
width: "full",
},
})

export const HeaderRow = styled("tr", {
base: {
position: "sticky",
top: 0,
zIndex: 1,
backgroundColor: "white",
},
})
export const HeaderCell = styled("th", {
base: {
padding: "8px 4px 12px",
fontWeight: "bold",
},
})
112 changes: 112 additions & 0 deletions app/(private)/accessibility/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"use client"

import { useState } from "react"

import {deleteBuildingAccessibility, deletePlaceAccessibility, searchAccessibilities} from "@/lib/apis/api"

import {AccessibilitySummary, BuildingAccessibility, PlaceAccessibility} from "@/lib/models/accessibility";
import * as S from "./page.style"
import AccessibilityRow from "@/(private)/accessibility/components/AccessibilityRow";

const limit = 10

export default function AccessibilityList() {
const [query, setQuery] = useState<string>("")
const [cursor, setCursor] = useState<string | undefined>(undefined)
const [accessibilitySummaries, setAccessibilitySummaries] = useState<AccessibilitySummary[]>([])

const searchAccessibilitiesWithNewQuery = () => {
searchAccessibilities(query, undefined, limit)
.then((res) => {
setAccessibilitySummaries(res.items)
setCursor(res.cursor)
})
}

const loadNextPage = () => {
searchAccessibilities(query, cursor, limit)
.then((res) => {
setAccessibilitySummaries([...accessibilitySummaries, ...res.items])
setCursor(res.cursor)
})
}

const handleDeletePlaceAccessibility = (accessibilitySummary: AccessibilitySummary) => {
const { id, placeName } = accessibilitySummary.placeAccessibility
if (!confirm(`정말 [${placeName}] 장소의 정보를 삭제하시겠습니까?`)) return
deletePlaceAccessibility({ id })
.then(() => {
setAccessibilitySummaries(accessibilitySummaries.filter((it) => it.placeAccessibility.id !== id))
})
}

const handleDeleteBuildingAccessibility = (accessibilitySummary: AccessibilitySummary) => {
const id = accessibilitySummary.buildingAccessibility?.id
const placeName = accessibilitySummary.placeAccessibility.placeName
if (!id) return
if (!confirm(`정말 [${placeName}] 장소의 건물 정보를 삭제하시겠습니까?`)) return
deleteBuildingAccessibility({ id })
.then(() => {
setAccessibilitySummaries(
accessibilitySummaries.map((it) => {
if (it.buildingAccessibility?.id !== id) {
return it
}
return {
placeAccessibility: it.placeAccessibility,
buildingAccessibility: undefined,
}
})
)
})
}

return (
<S.Page>
<S.PageTitle>
등록된 정보 관리
</S.PageTitle>
<input
type="text"
name="query"
placeholder="등록 최신순 검색"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<S.SearchButton
onClick={searchAccessibilitiesWithNewQuery}
>
검색
</S.SearchButton>
<S.TableWrapper>
<S.AccessibilityTable>
<S.HeaderRow>
<S.HeaderCell>장소명</S.HeaderCell>
<S.HeaderCell>정복자</S.HeaderCell>
<S.HeaderCell>정복 시점</S.HeaderCell>
<S.HeaderCell>장소 사진</S.HeaderCell>
<S.HeaderCell>건물 사진</S.HeaderCell>
<S.HeaderCell>삭제</S.HeaderCell>
</S.HeaderRow>
{accessibilitySummaries.map((accessibilitySummary) =>
<AccessibilityRow
accessibilitySummary={accessibilitySummary}
onDeletePlaceAccessibility={handleDeletePlaceAccessibility}
onDeleteBuildingAccessibility={handleDeleteBuildingAccessibility}
/>
)}
</S.AccessibilityTable>
</S.TableWrapper>
{
cursor
? <S.LoadNextPageButton
onClick={loadNextPage}
disabled={!cursor}
>
더 불러오기
</S.LoadNextPageButton>
: null
}
</S.Page>
)
}
1 change: 1 addition & 0 deletions app/layout/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default function Sidebar() {
<MenuItem href="/quest">퀘스트 관리</MenuItem>
<MenuItem href="/challenge">챌린지 관리</MenuItem>
<MenuItem href="/region">오픈 지역 관리</MenuItem>
<MenuItem href="/accessibility">등록된 정보 관리</MenuItem>
</S.Menu>
<Spacer />
<S.LogoutButton onClick={logout}>로그아웃</S.LogoutButton>
Expand Down
9 changes: 9 additions & 0 deletions app/modals/AccessibilityImage/AccessibilityImage.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { styled } from "@/styles/jsx"

export const AccessibilityImage = styled("img", {
base: {
height: "80%",
maxWidth: "1000px",
maxHeight: "1000px",
},
})
13 changes: 13 additions & 0 deletions app/modals/AccessibilityImage/AccessibilityImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { BasicModalProps } from "@reactleaf/modal"

import * as S from "./AccessibilityImage.style"

interface Props extends BasicModalProps {
imageUrl: string
}

export default function BuildingDetailSheet({ imageUrl }: Props) {
return (
<S.AccessibilityImage src={imageUrl} />
)
}
1 change: 1 addition & 0 deletions app/modals/register.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const register = {
AccessibilityImage: () => import("./AccessibilityImage/AccessibilityImage"),
BuildingDetailSheetMobile: () => import("./BuildingDetailSheet/BuildingDetailSheet.mobile"),
BuildingDetailSheetDesktop: () => import("./BuildingDetailSheet/BuildingDetailSheet.desktop"),
RegionCreate: () => import("./RegionCreate"),
Expand Down
34 changes: 34 additions & 0 deletions lib/apis/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { http } from "../http"
import { Challenge } from "../models/challenge"
import { QuestBuilding, QuestDetail, QuestSummary } from "../models/quest"
import { Region } from "../models/region"
import {AccessibilitySummary} from "@/lib/models/accessibility";
import {stringify} from "querystring";

export function useQuests() {
return useQuery({
Expand Down Expand Up @@ -138,3 +140,35 @@ export function deleteRegion({ id }: { id: string }) {
method: "DELETE",
})
}

export interface SearchAccessibilitiesResult {
items: AccessibilitySummary[]
cursor: string | undefined
}
export function searchAccessibilities(
query: string,
cursor: string | undefined,
limit: number | undefined,
): Promise<SearchAccessibilitiesResult> {
const params: {[key: string]: any} = { placeName: query };
if (cursor) {
params["cursor"] = cursor;
}
if (limit) {
params["limit"] = limit.toString();
}
return http(`/admin/accessibilities/search?${stringify(params)}`)
.then((res) => res.json())
}

export function deletePlaceAccessibility({ id }: { id: string }) {
return http(`/admin/place-accessibilities/${id}`, {
method: "DELETE",
})
}

export function deleteBuildingAccessibility({ id }: { id: string }) {
return http(`/admin/building-accessibilities/${id}`, {
method: "DELETE",
})
}
17 changes: 17 additions & 0 deletions lib/models/accessibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export interface AccessibilitySummary {
placeAccessibility: PlaceAccessibility
buildingAccessibility?: BuildingAccessibility
}

export interface PlaceAccessibility {
id: string
placeName: string
imageUrls: string[]
registeredUserName?: string
createdAtMillis: number
}

export interface BuildingAccessibility {
id: string
imageUrls: string[]
}

0 comments on commit 7df5ccc

Please sign in to comment.