Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions .github/workflows/deploy-preview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
name: Deploy preview

on:
push:
branches-ignore:
- main
- gh-pages

permissions:
contents: write

concurrency:
group: 'pages-preview-${{ github.ref_name }}'
cancel-in-progress: true

jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
cache-dependency-path: ./web/package-lock.json

- name: Sanitize branch name
id: branch
run: |
SAFE="${{ github.ref_name }}"
SAFE="${SAFE//\//-}"
echo "safe=$SAFE" >> $GITHUB_OUTPUT

- name: Install and Build
working-directory: ./web
run: |
npm ci
npm run build -- --base=/tasks/preview/${{ steps.branch.outputs.safe }}

- name: Deploy preview to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./web/dist
publish_branch: gh-pages
destination_dir: preview/${{ steps.branch.outputs.safe }}
13 changes: 11 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@ on:
branches: ['main']
workflow_dispatch:

# Only grant the necessary permissions
permissions:
contents: write # Required for peaceiris/actions-gh-pages to push to gh-pages branch
contents: write

concurrency:
group: 'pages'
Expand All @@ -33,6 +32,16 @@ jobs:
npm ci
npm run build

- name: Restore preview directory from gh-pages
run: |
git fetch origin gh-pages 2>/dev/null || true
if git rev-parse origin/gh-pages >/dev/null 2>&1; then
git checkout origin/gh-pages -- preview 2>/dev/null || true
if [ -d preview ]; then
cp -r preview ./web/dist/preview
fi
fi

- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
Expand Down
6 changes: 6 additions & 0 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 1 addition & 7 deletions web/src/components/AppRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@ import { getBookNamesMap, getChapterData } from '../utils/dataUtils'
import { groupByBook, groupByDay } from '../utils/groupUtils'
import { useApi } from './ApiContext'
import { createMemo } from 'solid-js'
import type { Tag } from '../data/model'

const OT_NT = {
OT: 3,
NT: 2,
} as Record<Tag, number>

export function AppRouter() {
const api = useApi()
Expand All @@ -20,7 +14,7 @@ export function AppRouter() {
const bookNames = getBookNamesMap(chapters)
const bookGroups = groupByBook(chapters)
const planGroups = createMemo(() =>
groupByDay(chapters, api.getTags(), OT_NT),
groupByDay(chapters, api.getTags(), api.perDayTagData(), api.targetDays()),
)

return (
Expand Down
35 changes: 35 additions & 0 deletions web/src/components/PlanSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,41 @@ export function PlanSettings() {
</For>
</ul>

<section class={styles.cutoffSection}>
<label>
Target days:
<input
type="number"
min={1}
value={api.targetDays()}
onInput={(e) => api.setTargetDays(+e.target.value)}
/>
</label>
<label>
Cutoff (days):
<input
type="number"
min={1}
value={api.cutoffDays() ?? ''}
onInput={(e) => {
const v = e.target.value
api.setCutoffDays(v === '' ? null : +v)
}}
/>
</label>
<label>
Cutoff (date):
<input
type="date"
value={api.cutoffDate() ?? ''}
onInput={(e) => {
const v = e.target.value
api.setCutoffDate(v === '' ? null : v)
}}
/>
</label>
</section>

<section class={styles.dataSection}>
<h2>Data</h2>
<div class={styles.dataButtons}>
Expand Down
25 changes: 23 additions & 2 deletions web/src/components/TagSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,33 @@ export function TagSelector(props: TagSelectorProps) {
}
}

const onRemove = (tagName: Tag) => {
return () => {
props.onChange({
...props.value,
tags: props.value.tags.filter((t) => t !== tagName),
})
}
}

return (
<div class={styles.TagSelector}>
<input type="number" />
<input
type="number"
value={props.value.count}
min={1}
onInput={(e) => props.onChange({ ...props.value, count: +e.target.value })}
/>
<div>
<ul class={styles.tagList}>
<For each={props.value.tags}>{(tagName) => <li>{tagName}</li>}</For>
<For each={props.value.tags}>
{(tagName) => (
<li>
{tagName}
<button type="button" onClick={onRemove(tagName)}>×</button>
</li>
)}
</For>
<li>
<input
class={styles.addTag}
Expand Down
84 changes: 70 additions & 14 deletions web/src/data/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,26 @@ export class Api {
this._tagsData = tagsData

// perDayTagData signal
const [perDayTagData, setPerDayTagData] = createSignal<PerDayTagData[]>([
{
tags: ['OT' as Tag],
count: 3,
},
{
tags: ['NT' as Tag],
count: 2,
},
])
const [perDayTagData, setPerDayTagData] = createSignal<PerDayTagData[]>(
settingsData.perDayTagData,
)
this.perDayTagData = perDayTagData
this.setPerDayTagData = setPerDayTagData
this._setPerDayTagData = setPerDayTagData

// targetDays signal
const [targetDays, setTargetDays] = createSignal(settingsData.targetDays)
this.targetDays = targetDays
this._setTargetDays = setTargetDays

// cutoffDays signal
const [cutoffDays, setCutoffDays] = createSignal<number | null>(settingsData.cutoffDays)
this.cutoffDays = cutoffDays
this._setCutoffDays = setCutoffDays

// cutoffDate signal
const [cutoffDate, setCutoffDate] = createSignal<string | null>(settingsData.cutoffDate)
this.cutoffDate = cutoffDate
this._setCutoffDate = setCutoffDate

// searchText signal
const [searchText, setSearchText] = createSignal('')
Expand All @@ -80,15 +88,38 @@ export class Api {
private readonly _settingsData: SettingsData
private readonly _tagsData: TagRecord
private readonly _setShowCompleted: Setter<boolean>
private readonly _setTargetDays: Setter<number>
private readonly _setCutoffDays: Setter<number | null>
private readonly _setCutoffDate: Setter<string | null>
private readonly _setPerDayTagData: Setter<PerDayTagData[]>
private readonly _setTimeStampMap: Setter<TimeStampMap>

readonly perDayTagData: Accessor<PerDayTagData[]>
readonly setPerDayTagData: Setter<PerDayTagData[]>
readonly targetDays: Accessor<number>
readonly cutoffDays: Accessor<number | null>
readonly cutoffDate: Accessor<string | null>
readonly searchText: Accessor<string>
readonly setSearchText: Setter<string>
readonly showCompleted: Accessor<boolean>
readonly timeStampMap: Accessor<TimeStampMap>

private currentSettings = (): SettingsData => ({
showCompleted: this.showCompleted(),
targetDays: this.targetDays(),
cutoffDays: this.cutoffDays(),
cutoffDate: this.cutoffDate(),
perDayTagData: this.perDayTagData(),
})

private effectiveCutoff = (): string | null => {
const days = this.cutoffDays()
const rolling = days != null
? new Date(Date.now() - days * 86400000).toISOString().slice(0, 10)
: null
const candidates = [rolling, this.cutoffDate()].filter(Boolean) as string[]
return candidates.length ? candidates.toSorted().at(-1)! : null
}

getChapterData = (): ChapterData[] => {
return this._chapterData
}
Expand Down Expand Up @@ -139,7 +170,10 @@ export class Api {
abbrev,
number,
}: Pick<ChapterData, 'abbrev' | 'number'>) => {
return this.getChapterDates({ abbrev, number }).length > 0
const dates = this.getChapterDates({ abbrev, number })
const cutoff = this.effectiveCutoff()
if (!cutoff) return dates.length > 0
return dates.some((d) => d.slice(0, 10) >= cutoff)
}

completeCount = (chapters: ChapterData[]): number => {
Expand Down Expand Up @@ -173,9 +207,31 @@ export class Api {
deleteTimeStamp(this._db, book, chapter, date)
}

setPerDayTagData = async (
valueOrUpdater: PerDayTagData[] | ((prev: PerDayTagData[]) => PerDayTagData[]),
) => {
this._setPerDayTagData(valueOrUpdater as PerDayTagData[])
await updateSettings(this._db, this.currentSettings())
}

setTargetDays = async (value: number) => {
this._setTargetDays(value)
await updateSettings(this._db, this.currentSettings())
}

setCutoffDays = async (value: number | null) => {
this._setCutoffDays(value)
await updateSettings(this._db, this.currentSettings())
}

setCutoffDate = async (value: string | null) => {
this._setCutoffDate(value)
await updateSettings(this._db, this.currentSettings())
}

toggleShowCompleted = async () => {
this._setShowCompleted((prev) => !prev)
await updateSettings(this._db, this.showCompleted())
await updateSettings(this._db, this.currentSettings())
}

exportData = (): string => {
Expand Down
17 changes: 15 additions & 2 deletions web/src/data/indexDb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import type {
BookAbbrev,
ChapterID,
ISODateTimeString,
PerDayTagData,
SettingsData,
Tag,
TimeStampMap,
} from './model'

Expand All @@ -14,6 +16,10 @@ export type TimeStampKey = `${ISODateTimeString}_${BookAbbrev}_${ChapterID}`
interface SettingsRecord {
id: '1'
showCompleted: boolean
targetDays?: number
cutoffDays?: number | null
cutoffDate?: string | null
perDayTagData?: PerDayTagData[]
}

interface TimestampRecord {
Expand Down Expand Up @@ -110,10 +116,10 @@ export async function addTimestamp(
}

/** Update the settings record */
export async function updateSettings(db: IDBDatabase, showCompleted: boolean) {
export async function updateSettings(db: IDBDatabase, settings: SettingsData) {
const record: SettingsRecord = {
id: '1',
showCompleted,
...settings,
}

return putRecord(db, SETTINGS_STORE_NAME, record)
Expand Down Expand Up @@ -157,6 +163,13 @@ export async function getSettingsData(db: IDBDatabase): Promise<SettingsData> {
const result = (event.target as IDBRequest<SettingsRecord>).result
resolve({
showCompleted: result?.showCompleted ?? true,
targetDays: result?.targetDays ?? 365,
cutoffDays: result?.cutoffDays ?? null,
cutoffDate: result?.cutoffDate ?? null,
perDayTagData: result?.perDayTagData ?? [
{ tags: ['OT' as Tag], count: 3 },
{ tags: ['NT' as Tag], count: 2 },
],
})
}

Expand Down
Loading
Loading