Fix responsive layout and header icons
This commit is contained in:
1
assets/icons/search.svg
Normal file
1
assets/icons/search.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M784-120 532-372q-30 24-69 38t-83 14q-109 0-184.5-75.5T120-580q0-109 75.5-184.5T380-840q109 0 184.5 75.5T640-580q0 44-14 83t-38 69l252 252-56 56ZM380-400q75 0 127.5-52.5T560-580q0-75-52.5-127.5T380-760q-75 0-127.5 52.5T200-580q0 75 52.5 127.5T380-400Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 376 B |
1
assets/icons/tune.svg
Normal file
1
assets/icons/tune.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M440-120v-240h80v80h320v80H520v80h-80Zm-320-80v-80h240v80H120Zm160-160v-80H120v-80h160v-80h80v240h-80Zm160-80v-80h400v80H440Zm160-160v-240h80v80h160v80H680v80h-80Zm-480-80v-80h400v80H120Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 312 B |
@@ -81,3 +81,7 @@
|
|||||||
- 사용자 설정 테이블과 API를 추가하고, 설정 화면에서 주 화폐, 월 시작일, 주간 시작일을 저장할 수 있도록 했다.
|
- 사용자 설정 테이블과 API를 추가하고, 설정 화면에서 주 화폐, 월 시작일, 주간 시작일을 저장할 수 있도록 했다.
|
||||||
- 월별 거래/자산 조회 범위가 사용자 설정의 월 시작일을 따르도록 연결했다.
|
- 월별 거래/자산 조회 범위가 사용자 설정의 월 시작일을 따르도록 연결했다.
|
||||||
- 내역 화면의 주간 날짜 스트립 요일 순서와 주 시작 계산이 사용자 설정의 주간 시작일을 따르도록 연결했다.
|
- 내역 화면의 주간 날짜 스트립 요일 순서와 주 시작 계산이 사용자 설정의 주간 시작일을 따르도록 연결했다.
|
||||||
|
- 로그인 화면에서 PC 사이드바 여백이 적용되어 화면이 오른쪽으로 밀리는 문제를 수정했다.
|
||||||
|
- PC 중간 폭 화면에서 콘텐츠 오른쪽이 잘리지 않도록 전역 레이아웃을 margin 기반에서 padding 기반으로 변경했다.
|
||||||
|
- 내역 헤더의 검색과 필터 버튼을 `assets/icons/search.svg`, `assets/icons/tune.svg` 인라인 SVG로 교체했다.
|
||||||
|
- CSV 내보내기 컬럼을 한글 컬럼명과 앱의 거래 흐름에 맞는 순서로 정리하고 Excel 호환 BOM을 추가했다.
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
- 통계 화면은 그래프보다 숫자와 분류별 리스트를 우선한다.
|
- 통계 화면은 그래프보다 숫자와 분류별 리스트를 우선한다.
|
||||||
- 방향 이동 UI는 `assets/icons`의 키보드 화살표 SVG를 사용한다.
|
- 방향 이동 UI는 `assets/icons`의 키보드 화살표 SVG를 사용한다.
|
||||||
- 추가 버튼 UI는 텍스트 `+` 대신 `assets/icons/add.svg`를 사용한다.
|
- 추가 버튼 UI는 텍스트 `+` 대신 `assets/icons/add.svg`를 사용한다.
|
||||||
|
- 검색과 필터 같은 헤더 액션도 텍스트 기호 대신 `assets/icons`의 SVG 아이콘을 사용한다.
|
||||||
- 거래 목록의 카테고리 표시는 하위 카테고리가 있으면 하위 이름만, 없으면 상위 이름만 보여준다.
|
- 거래 목록의 카테고리 표시는 하위 카테고리가 있으면 하위 이름만, 없으면 상위 이름만 보여준다.
|
||||||
- 카테고리 아이콘과 색상은 카테고리 관리 상세 화면에서 수정하고 거래 목록 아이콘에 즉시 반영한다.
|
- 카테고리 아이콘과 색상은 카테고리 관리 상세 화면에서 수정하고 거래 목록 아이콘에 즉시 반영한다.
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@
|
|||||||
- 자산은 활성/비활성으로 관리하지 않는다. 삭제는 거래가 연결되지 않은 자산에 한해 제공한다.
|
- 자산은 활성/비활성으로 관리하지 않는다. 삭제는 거래가 연결되지 않은 자산에 한해 제공한다.
|
||||||
- 거래에 연결되는 카테고리 선택은 하위가 있으면 하위 카테고리를 우선하고, 하위가 없는 경우에만 상위 카테고리를 직접 선택한다.
|
- 거래에 연결되는 카테고리 선택은 하위가 있으면 하위 카테고리를 우선하고, 하위가 없는 경우에만 상위 카테고리를 직접 선택한다.
|
||||||
- CSV Export는 데이터 보존을 위한 필수 기능으로 유지한다.
|
- CSV Export는 데이터 보존을 위한 필수 기능으로 유지한다.
|
||||||
|
- CSV Export 컬럼은 사용자가 앱에서 보는 흐름에 맞춰 날짜, 유형, 자산, 분류, 금액, 내용, 메모 중심의 한글 컬럼으로 제공한다.
|
||||||
- 주 화폐, 월 시작일, 주간 시작일 같은 사용자별 기본 설정은 `UserSetting`에 저장한다.
|
- 주 화폐, 월 시작일, 주간 시작일 같은 사용자별 기본 설정은 `UserSetting`에 저장한다.
|
||||||
|
|
||||||
## 개발/운영 기준
|
## 개발/운영 기준
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<main class="min-h-dvh w-full bg-white md:ml-64 md:bg-paper" :class="showNav ? 'pb-20 md:pb-0' : ''">
|
<main class="min-h-dvh w-full bg-white md:bg-paper" :class="showNav ? 'pb-20 md:pb-0 md:pl-64' : ''">
|
||||||
<div class="mx-auto min-h-dvh w-full md:max-w-7xl md:bg-transparent">
|
<div class="mx-auto min-h-dvh w-full md:max-w-7xl md:bg-transparent">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,9 +7,21 @@
|
|||||||
</button>
|
</button>
|
||||||
<h1 class="text-lg font-extrabold">최근 이용 내역</h1>
|
<h1 class="text-lg font-extrabold">최근 이용 내역</h1>
|
||||||
<div class="flex items-center gap-1">
|
<div class="flex items-center gap-1">
|
||||||
<button type="button" class="flex h-9 w-9 items-center justify-center rounded-md text-2xl leading-none active:bg-paper" aria-label="검색" @click="searchOpen = !searchOpen">⌕</button>
|
<button
|
||||||
<button type="button" class="relative flex h-9 w-9 items-center justify-center rounded-md text-xl leading-none active:bg-paper" aria-label="필터" @click="filterSheetOpen = true">
|
type="button"
|
||||||
☷
|
class="flex h-9 w-9 items-center justify-center rounded-md text-ink active:bg-paper"
|
||||||
|
aria-label="검색"
|
||||||
|
@click="searchOpen = !searchOpen"
|
||||||
|
>
|
||||||
|
<span class="nav-icon h-6 w-6" v-html="searchIcon" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="relative flex h-9 w-9 items-center justify-center rounded-md text-ink active:bg-paper"
|
||||||
|
aria-label="필터"
|
||||||
|
@click="filterSheetOpen = true"
|
||||||
|
>
|
||||||
|
<span class="nav-icon h-6 w-6" v-html="tuneIcon" />
|
||||||
<span v-if="activeFilterCount" class="absolute right-1 top-1 h-2 w-2 rounded-full bg-red-500" />
|
<span v-if="activeFilterCount" class="absolute right-1 top-1 h-2 w-2 rounded-full bg-red-500" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -176,8 +188,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { addDays, addMonths, format, parse, startOfWeek } from 'date-fns'
|
import { addDays, addMonths, format, parse, startOfWeek } from 'date-fns'
|
||||||
import type { WeekStartsOn } from 'date-fns'
|
import type { WeekStartsOn } from 'date-fns'
|
||||||
|
import searchIconRaw from '~/assets/icons/search.svg?raw'
|
||||||
|
import tuneIconRaw from '~/assets/icons/tune.svg?raw'
|
||||||
import type { Transaction } from '~/composables/useMoney'
|
import type { Transaction } from '~/composables/useMoney'
|
||||||
|
|
||||||
|
const inlineIcon = (icon: string) => icon
|
||||||
|
.replace(/fill="[^"]*"/g, 'fill="currentColor"')
|
||||||
|
.replace(/height="[^"]*"/, 'height="100%"')
|
||||||
|
.replace(/width="[^"]*"/, 'width="100%"')
|
||||||
|
const searchIcon = inlineIcon(searchIconRaw)
|
||||||
|
const tuneIcon = inlineIcon(tuneIconRaw)
|
||||||
const selectedDate = ref(formatDateOnly())
|
const selectedDate = ref(formatDateOnly())
|
||||||
const weekSwipeStartX = ref(0)
|
const weekSwipeStartX = ref(0)
|
||||||
const weekDirection = ref<'next' | 'prev'>('next')
|
const weekDirection = ref<'next' | 'prev'>('next')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { setHeader } from 'h3'
|
import { setHeader } from 'h3'
|
||||||
|
import { format } from 'date-fns'
|
||||||
import { prisma } from '../../utils/db'
|
import { prisma } from '../../utils/db'
|
||||||
import { requireUser } from '../../utils/auth'
|
import { requireUser } from '../../utils/auth'
|
||||||
|
|
||||||
@@ -11,23 +12,26 @@ export default defineEventHandler(async (event) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const escape = (value: unknown) => `"${String(value ?? '').replaceAll('"', '""')}"`
|
const escape = (value: unknown) => `"${String(value ?? '').replaceAll('"', '""')}"`
|
||||||
|
const categoryName = (row: typeof rows[number]) => row.category.name
|
||||||
|
const parentCategoryName = (row: typeof rows[number]) => row.category.parent?.name || ''
|
||||||
const lines = [
|
const lines = [
|
||||||
['id', 'type', 'occurredAt', 'asset', 'category', 'amount', 'title', 'memo', 'latitude', 'longitude'].map(escape).join(','),
|
['날짜', '유형', '자산', '상위분류', '분류', '금액', '내용', '메모', '위도', '경도', '거래ID'].map(escape).join(','),
|
||||||
...rows.map(row => [
|
...rows.map(row => [
|
||||||
row.id,
|
format(row.occurredAt, 'yyyy-MM-dd'),
|
||||||
row.type,
|
row.type === 'income' ? '수입' : '지출',
|
||||||
row.occurredAt.toISOString(),
|
|
||||||
row.asset.name,
|
row.asset.name,
|
||||||
row.category.parent ? `${row.category.parent.name} > ${row.category.name}` : row.category.name,
|
parentCategoryName(row),
|
||||||
|
categoryName(row),
|
||||||
row.amount,
|
row.amount,
|
||||||
row.title,
|
row.title,
|
||||||
row.memo,
|
row.memo,
|
||||||
row.latitude,
|
row.latitude,
|
||||||
row.longitude
|
row.longitude,
|
||||||
|
row.id
|
||||||
].map(escape).join(','))
|
].map(escape).join(','))
|
||||||
]
|
]
|
||||||
|
|
||||||
setHeader(event, 'content-type', 'text/csv; charset=utf-8')
|
setHeader(event, 'content-type', 'text/csv; charset=utf-8')
|
||||||
setHeader(event, 'content-disposition', 'attachment; filename="transactions.csv"')
|
setHeader(event, 'content-disposition', 'attachment; filename="money-transactions.csv"')
|
||||||
return lines.join('\n')
|
return `\uFEFF${lines.join('\n')}`
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user