summaryrefslogtreecommitdiff
path: root/app/lib
diff options
context:
space:
mode:
Diffstat (limited to 'app/lib')
-rw-r--r--app/lib/db.server.ts80
-rw-r--r--app/lib/utils.ts7
2 files changed, 87 insertions, 0 deletions
diff --git a/app/lib/db.server.ts b/app/lib/db.server.ts
index cb51028..d3e5469 100644
--- a/app/lib/db.server.ts
+++ b/app/lib/db.server.ts
@@ -200,6 +200,86 @@ export interface ArtistRevision {
created_at: string;
}
+export interface MemberGroup {
+ artist_id: string;
+ artist_name: string;
+ artist_slug: string;
+ periods: BandMemberRow[];
+ is_current: boolean;
+ duration_months: number | null;
+}
+
+export interface BandGroup {
+ band_id: string;
+ band_name: string;
+ band_slug: string;
+ periods: ArtistMemberRow[];
+ is_current: boolean;
+ duration_months: number | null;
+}
+
+function parseYearMonth(s: string): { year: number; month: number } | null {
+ const m = s.match(/^(\d{4})-(\d{2})$/);
+ if (!m) return null;
+ return { year: parseInt(m[1]), month: parseInt(m[2]) };
+}
+
+function calcDurationMonths(since: string, until: string): number | null {
+ const from = parseYearMonth(since);
+ if (!from) return null;
+ const today = new Date();
+ const to = parseYearMonth(until) ?? { year: today.getFullYear(), month: today.getMonth() + 1 };
+ return Math.max(0, (to.year * 12 + to.month) - (from.year * 12 + from.month));
+}
+
+export function groupBandMembers(members: BandMemberRow[]): {
+ current: MemberGroup[];
+ former: MemberGroup[];
+ all: MemberGroup[];
+} {
+ const byArtist = new Map<string, BandMemberRow[]>();
+ for (const m of members) {
+ const list = byArtist.get(m.artist_id) ?? [];
+ list.push(m);
+ byArtist.set(m.artist_id, list);
+ }
+ const all: MemberGroup[] = [];
+ for (const [artistId, periods] of byArtist) {
+ const first = periods[0];
+ const is_current = periods.some((p) => !p.until);
+ const duration_months = periods.reduce<number | null>((acc, p) => {
+ const d = calcDurationMonths(p.since, p.until);
+ return d === null ? acc : (acc ?? 0) + d;
+ }, null);
+ all.push({ artist_id: artistId, artist_name: first.artist_name, artist_slug: first.artist_slug, periods, is_current, duration_months });
+ }
+ return { current: all.filter((g) => g.is_current), former: all.filter((g) => !g.is_current), all };
+}
+
+export function groupArtistMembers(members: ArtistMemberRow[]): {
+ current: BandGroup[];
+ former: BandGroup[];
+ all: BandGroup[];
+} {
+ const byBand = new Map<string, ArtistMemberRow[]>();
+ for (const m of members) {
+ const list = byBand.get(m.band_id) ?? [];
+ list.push(m);
+ byBand.set(m.band_id, list);
+ }
+ const all: BandGroup[] = [];
+ for (const [bandId, periods] of byBand) {
+ const first = periods[0];
+ const is_current = periods.some((p) => !p.until);
+ const duration_months = periods.reduce<number | null>((acc, p) => {
+ const d = calcDurationMonths(p.since, p.until);
+ return d === null ? acc : (acc ?? 0) + d;
+ }, null);
+ all.push({ band_id: bandId, band_name: first.band_name, band_slug: first.band_slug, periods, is_current, duration_months });
+ }
+ return { current: all.filter((g) => g.is_current), former: all.filter((g) => !g.is_current), all };
+}
+
export function getIpAddress(request: Request): string {
return (
request.headers.get("x-forwarded-for")?.split(",")[0].trim() ??
diff --git a/app/lib/utils.ts b/app/lib/utils.ts
new file mode 100644
index 0000000..961aa31
--- /dev/null
+++ b/app/lib/utils.ts
@@ -0,0 +1,7 @@
+export function formatDuration(months: number): string {
+ const years = Math.floor(months / 12);
+ const m = months % 12;
+ if (years === 0) return `${m}ヶ月`;
+ if (m === 0) return `${years}年`;
+ return `${years}年${m}ヶ月`;
+}