Initial commit

This commit is contained in:
kihong.kim
2026-01-24 19:41:19 +09:00
commit 807df3d678
90 changed files with 6411 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
import "../config/api_config.dart";
import "../models/announcement.dart";
import "api_client.dart";
import "mock_data.dart";
class AnnouncementService {
final ApiClient _client;
AnnouncementService(this._client);
Future<List<Announcement>> fetchAnnouncements({
bool activeOnly = false,
}) async {
if (ApiConfig.useMockData) {
final items = List<Announcement>.from(MockDataStore.announcements);
if (activeOnly) {
return items.where((item) => item.active).toList();
}
return items;
}
final query = activeOnly ? {"active": "true"} : null;
final data = await _client.getList(ApiConfig.announcements, query: query);
return data
.map((item) => Announcement.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<Announcement> createAnnouncement(Announcement announcement) async {
if (ApiConfig.useMockData) {
final created = Announcement(
id: "announcement-${DateTime.now().millisecondsSinceEpoch}",
title: announcement.title,
content: announcement.content,
priority: announcement.priority,
active: announcement.active,
);
MockDataStore.announcements.add(created);
return created;
}
final data = await _client.post(
ApiConfig.announcements,
announcement.toJson(),
);
return Announcement.fromJson(data);
}
Future<Announcement> updateAnnouncement(Announcement announcement) async {
if (ApiConfig.useMockData) {
final index = MockDataStore.announcements.indexWhere(
(item) => item.id == announcement.id,
);
if (index != -1) {
MockDataStore.announcements[index] = announcement;
}
return announcement;
}
final data = await _client.put(
"${ApiConfig.announcements}/${announcement.id}",
announcement.toJson(),
);
return Announcement.fromJson(data);
}
Future<void> deleteAnnouncement(String id) async {
if (ApiConfig.useMockData) {
MockDataStore.announcements.removeWhere((item) => item.id == id);
return;
}
await _client.delete("${ApiConfig.announcements}/$id");
}
}

View File

@@ -0,0 +1,71 @@
import "dart:convert";
import "package:http/http.dart" as http;
import "../config/api_config.dart";
class ApiClient {
final http.Client _client;
ApiClient({http.Client? client}) : _client = client ?? http.Client();
Uri _uri(String path, [Map<String, dynamic>? query]) {
return Uri.parse(ApiConfig.baseUrl).replace(
path: path,
queryParameters: query?.map((key, value) => MapEntry(key, "$value")),
);
}
Future<List<dynamic>> getList(
String path, {
Map<String, dynamic>? query,
}) async {
final response = await _client.get(_uri(path, query));
_ensureSuccess(response);
return jsonDecode(response.body) as List<dynamic>;
}
Future<Map<String, dynamic>> getMap(
String path, {
Map<String, dynamic>? query,
}) async {
final response = await _client.get(_uri(path, query));
_ensureSuccess(response);
return jsonDecode(response.body) as Map<String, dynamic>;
}
Future<Map<String, dynamic>> post(
String path,
Map<String, dynamic> body,
) async {
final response = await _client.post(
_uri(path),
headers: {"Content-Type": "application/json"},
body: jsonEncode(body),
);
_ensureSuccess(response);
return jsonDecode(response.body) as Map<String, dynamic>;
}
Future<Map<String, dynamic>> put(
String path,
Map<String, dynamic> body,
) async {
final response = await _client.put(
_uri(path),
headers: {"Content-Type": "application/json"},
body: jsonEncode(body),
);
_ensureSuccess(response);
return jsonDecode(response.body) as Map<String, dynamic>;
}
Future<void> delete(String path) async {
final response = await _client.delete(_uri(path));
_ensureSuccess(response);
}
void _ensureSuccess(http.Response response) {
if (response.statusCode < 200 || response.statusCode >= 300) {
throw Exception("Request failed: ${response.statusCode}");
}
}
}

View File

@@ -0,0 +1,26 @@
import "../config/api_config.dart";
import "../models/bible_verse.dart";
import "api_client.dart";
import "mock_data.dart";
class BibleService {
final ApiClient _client;
BibleService(this._client);
Future<BibleVerse> fetchTodayVerse({String? date}) async {
if (ApiConfig.useMockData) {
final verses = MockDataStore.bibleVerses;
if (verses.isEmpty) {
return MockDataStore.bible;
}
verses.shuffle();
return verses.first;
}
final data = await _client.getMap(
ApiConfig.bibleToday,
query: date == null ? null : {"date": date},
);
return BibleVerse.fromJson(data);
}
}

View File

@@ -0,0 +1,66 @@
import "../config/api_config.dart";
import "../models/bible_verse.dart";
import "api_client.dart";
import "mock_data.dart";
class BibleVerseService {
final ApiClient _client;
BibleVerseService(this._client);
Future<List<BibleVerse>> fetchVerses({bool activeOnly = false}) async {
if (ApiConfig.useMockData) {
final items = List<BibleVerse>.from(MockDataStore.bibleVerses);
if (activeOnly) {
return items.where((item) => item.active).toList();
}
return items;
}
final query = activeOnly ? {"active": "true"} : null;
final data = await _client.getList(ApiConfig.bibleVerses, query: query);
return data
.map((item) => BibleVerse.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<BibleVerse> createVerse(BibleVerse verse) async {
if (ApiConfig.useMockData) {
final created = BibleVerse(
id: "bible-${DateTime.now().millisecondsSinceEpoch}",
text: verse.text,
reference: verse.reference,
date: verse.date,
active: verse.active,
);
MockDataStore.bibleVerses.add(created);
return created;
}
final data = await _client.post(ApiConfig.bibleVerses, verse.toJson());
return BibleVerse.fromJson(data);
}
Future<BibleVerse> updateVerse(BibleVerse verse) async {
if (ApiConfig.useMockData) {
final index = MockDataStore.bibleVerses.indexWhere(
(item) => item.id == verse.id,
);
if (index != -1) {
MockDataStore.bibleVerses[index] = verse;
}
return verse;
}
final data = await _client.put(
"${ApiConfig.bibleVerses}/${verse.id}",
verse.toJson(),
);
return BibleVerse.fromJson(data);
}
Future<void> deleteVerse(String id) async {
if (ApiConfig.useMockData) {
MockDataStore.bibleVerses.removeWhere((item) => item.id == id);
return;
}
await _client.delete("${ApiConfig.bibleVerses}/$id");
}
}

View File

@@ -0,0 +1,71 @@
import "../config/api_config.dart";
import "../models/family_member.dart";
import "api_client.dart";
import "mock_data.dart";
class FamilyService {
final ApiClient _client;
FamilyService(this._client);
Future<List<FamilyMember>> fetchFamilyMembers() async {
if (ApiConfig.useMockData) {
return List<FamilyMember>.from(MockDataStore.familyMembers);
}
final data = await _client.getList(ApiConfig.family);
return data
.map((item) => FamilyMember.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<FamilyMember> createFamilyMember(FamilyMember member) async {
if (ApiConfig.useMockData) {
final created = FamilyMember(
id: "family-${DateTime.now().millisecondsSinceEpoch}",
name: member.name,
emoji: member.emoji,
color: member.color,
order: member.order,
);
MockDataStore.familyMembers.add(created);
return created;
}
final data = await _client.post(ApiConfig.family, member.toJson());
return FamilyMember.fromJson(data);
}
Future<FamilyMember> updateFamilyMember(FamilyMember member) async {
if (ApiConfig.useMockData) {
final index = MockDataStore.familyMembers.indexWhere(
(item) => item.id == member.id,
);
if (index != -1) {
MockDataStore.familyMembers[index] = member;
}
return member;
}
final data = await _client.put(
"${ApiConfig.family}/${member.id}",
member.toJson(),
);
return FamilyMember.fromJson(data);
}
Future<void> deleteFamilyMember(String id) async {
if (ApiConfig.useMockData) {
MockDataStore.familyMembers.removeWhere((item) => item.id == id);
return;
}
await _client.delete("${ApiConfig.family}/$id");
}
Future<List<FamilyMember>> fetchMembers() => fetchFamilyMembers();
Future<FamilyMember> createMember(FamilyMember member) =>
createFamilyMember(member);
Future<FamilyMember> updateMember(FamilyMember member) =>
updateFamilyMember(member);
Future<void> deleteMember(String id) => deleteFamilyMember(id);
}

View File

@@ -0,0 +1,149 @@
import "../models/announcement.dart";
import "../models/bible_verse.dart";
import "../models/family_member.dart";
import "../models/photo.dart";
import "../models/schedule_item.dart";
import "../models/todo_item.dart";
import "../models/weather_info.dart";
class MockDataStore {
static final List<FamilyMember> familyMembers = [
const FamilyMember(
id: "family-1",
name: "Dad",
emoji: ":)",
color: "#0F766E",
order: 1,
),
const FamilyMember(
id: "family-2",
name: "Mom",
emoji: "<3",
color: "#C2410C",
order: 2,
),
const FamilyMember(
id: "family-3",
name: "Son",
emoji: ":D",
color: "#1D4ED8",
order: 3,
),
const FamilyMember(
id: "family-4",
name: "Daughter",
emoji: ":-)",
color: "#7C3AED",
order: 4,
),
];
static final List<TodoItem> todos = [
TodoItem(
id: "todo-1",
familyMemberId: "family-1",
title: "Grocery run",
completed: false,
dueDate: DateTime.now(),
),
TodoItem(
id: "todo-2",
familyMemberId: "family-2",
title: "Team meeting",
completed: false,
dueDate: DateTime.now(),
),
TodoItem(
id: "todo-3",
familyMemberId: "family-3",
title: "Math homework",
completed: false,
dueDate: DateTime.now(),
),
TodoItem(
id: "todo-4",
familyMemberId: "family-4",
title: "Piano lesson",
completed: false,
dueDate: DateTime.now().add(const Duration(days: 1)),
),
];
static final List<ScheduleItem> schedules = [
ScheduleItem(
id: "schedule-1",
title: "Family dinner",
description: "Everyone at home",
startDate: DateTime.now(),
endDate: DateTime.now().add(const Duration(hours: 2)),
familyMemberId: "family-1",
isAllDay: false,
),
ScheduleItem(
id: "schedule-2",
title: "Soccer practice",
description: "School field",
startDate: DateTime.now().add(const Duration(hours: 3)),
endDate: DateTime.now().add(const Duration(hours: 4)),
familyMemberId: "family-3",
isAllDay: false,
),
];
static final List<Announcement> announcements = [
const Announcement(
id: "announcement-1",
title: "Weekend trip",
content: "Pack light and be ready by 8 AM",
priority: 2,
active: true,
),
const Announcement(
id: "announcement-2",
title: "Trash day",
content: "Take out bins tonight",
priority: 1,
active: true,
),
];
static final List<Photo> photos = [
const Photo(
id: "photo-1",
url: "https://picsum.photos/1200/800?random=21",
caption: "Summer vacation",
active: true,
),
const Photo(
id: "photo-2",
url: "https://picsum.photos/1200/800?random=22",
caption: "Family hike",
active: true,
),
const Photo(
id: "photo-3",
url: "https://picsum.photos/1200/800?random=23",
caption: "Birthday party",
active: true,
),
];
static WeatherInfo weather = const WeatherInfo(
description: "clear sky",
temperature: 12,
icon: "01d",
city: "Seoul",
);
static final List<BibleVerse> bibleVerses = [
const BibleVerse(
id: "bible-1",
text: "여호와를 경외하는 것이 지식의 근본이니라.",
reference: "잠언 1:7",
date: null,
active: true,
),
];
static BibleVerse bible = bibleVerses.first;
}

View File

@@ -0,0 +1,48 @@
import "../config/api_config.dart";
import "../models/photo.dart";
import "api_client.dart";
import "mock_data.dart";
class PhotoService {
final ApiClient _client;
PhotoService(this._client);
Future<List<Photo>> fetchPhotos({bool activeOnly = false}) async {
if (ApiConfig.useMockData) {
final items = List<Photo>.from(MockDataStore.photos);
if (activeOnly) {
return items.where((item) => item.active).toList();
}
return items;
}
final query = activeOnly ? {"active": "true"} : null;
final data = await _client.getList(ApiConfig.photos, query: query);
return data
.map((item) => Photo.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<Photo> createPhoto(Photo photo) async {
if (ApiConfig.useMockData) {
final created = Photo(
id: "photo-${DateTime.now().millisecondsSinceEpoch}",
url: photo.url,
caption: photo.caption,
active: photo.active,
);
MockDataStore.photos.add(created);
return created;
}
final data = await _client.post(ApiConfig.photos, photo.toJson());
return Photo.fromJson(data);
}
Future<void> deletePhoto(String id) async {
if (ApiConfig.useMockData) {
MockDataStore.photos.removeWhere((item) => item.id == id);
return;
}
await _client.delete("${ApiConfig.photos}/$id");
}
}

View File

@@ -0,0 +1,83 @@
import "../config/api_config.dart";
import "../models/schedule_item.dart";
import "api_client.dart";
import "mock_data.dart";
class ScheduleService {
final ApiClient _client;
ScheduleService(this._client);
Future<List<ScheduleItem>> fetchSchedules() async {
if (ApiConfig.useMockData) {
return List<ScheduleItem>.from(MockDataStore.schedules);
}
final data = await _client.getList(ApiConfig.schedules);
return data
.map((item) => ScheduleItem.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<List<ScheduleItem>> fetchWeeklySchedules() async {
if (ApiConfig.useMockData) {
return List<ScheduleItem>.from(MockDataStore.schedules);
}
final data = await _client.getList("${ApiConfig.schedules}/week");
return data
.map((item) => ScheduleItem.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<List<ScheduleItem>> fetchMonthlySchedules() async {
if (ApiConfig.useMockData) {
return List<ScheduleItem>.from(MockDataStore.schedules);
}
final data = await _client.getList("${ApiConfig.schedules}/month");
return data
.map((item) => ScheduleItem.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<ScheduleItem> createSchedule(ScheduleItem schedule) async {
if (ApiConfig.useMockData) {
final created = ScheduleItem(
id: "schedule-${DateTime.now().millisecondsSinceEpoch}",
title: schedule.title,
description: schedule.description,
startDate: schedule.startDate,
endDate: schedule.endDate,
familyMemberId: schedule.familyMemberId,
isAllDay: schedule.isAllDay,
);
MockDataStore.schedules.add(created);
return created;
}
final data = await _client.post(ApiConfig.schedules, schedule.toJson());
return ScheduleItem.fromJson(data);
}
Future<ScheduleItem> updateSchedule(ScheduleItem schedule) async {
if (ApiConfig.useMockData) {
final index = MockDataStore.schedules.indexWhere(
(item) => item.id == schedule.id,
);
if (index != -1) {
MockDataStore.schedules[index] = schedule;
}
return schedule;
}
final data = await _client.put(
"${ApiConfig.schedules}/${schedule.id}",
schedule.toJson(),
);
return ScheduleItem.fromJson(data);
}
Future<void> deleteSchedule(String id) async {
if (ApiConfig.useMockData) {
MockDataStore.schedules.removeWhere((item) => item.id == id);
return;
}
await _client.delete("${ApiConfig.schedules}/$id");
}
}

View File

@@ -0,0 +1,79 @@
import "../config/api_config.dart";
import "../models/todo_item.dart";
import "api_client.dart";
import "mock_data.dart";
class TodoService {
final ApiClient _client;
TodoService(this._client);
Future<List<TodoItem>> fetchTodos() async {
if (ApiConfig.useMockData) {
return List<TodoItem>.from(MockDataStore.todos);
}
final data = await _client.getList(ApiConfig.todos);
return data
.map((item) => TodoItem.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<List<TodoItem>> fetchTodayTodos() async {
if (ApiConfig.useMockData) {
final today = DateTime.now();
return MockDataStore.todos.where((todo) => todo.dueDate != null).where((
todo,
) {
final date = todo.dueDate!;
return date.year == today.year &&
date.month == today.month &&
date.day == today.day;
}).toList();
}
final data = await _client.getList("${ApiConfig.todos}/today");
return data
.map((item) => TodoItem.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<TodoItem> createTodo(TodoItem todo) async {
if (ApiConfig.useMockData) {
final created = TodoItem(
id: "todo-${DateTime.now().millisecondsSinceEpoch}",
familyMemberId: todo.familyMemberId,
title: todo.title,
completed: todo.completed,
dueDate: todo.dueDate,
);
MockDataStore.todos.add(created);
return created;
}
final data = await _client.post(ApiConfig.todos, todo.toJson());
return TodoItem.fromJson(data);
}
Future<TodoItem> updateTodo(TodoItem todo) async {
if (ApiConfig.useMockData) {
final index = MockDataStore.todos.indexWhere(
(item) => item.id == todo.id,
);
if (index != -1) {
MockDataStore.todos[index] = todo;
}
return todo;
}
final data = await _client.put(
"${ApiConfig.todos}/${todo.id}",
todo.toJson(),
);
return TodoItem.fromJson(data);
}
Future<void> deleteTodo(String id) async {
if (ApiConfig.useMockData) {
MockDataStore.todos.removeWhere((item) => item.id == id);
return;
}
await _client.delete("${ApiConfig.todos}/$id");
}
}

View File

@@ -0,0 +1,19 @@
import "../config/api_config.dart";
import "../models/weather_info.dart";
import "api_client.dart";
import "mock_data.dart";
class WeatherService {
final ApiClient _client;
WeatherService(this._client);
Future<WeatherInfo> fetchWeather({String? city}) async {
if (ApiConfig.useMockData) {
return MockDataStore.weather;
}
final query = city != null ? {"q": city} : null;
final data = await _client.getMap(ApiConfig.weather, query: query);
return WeatherInfo.fromJson(data);
}
}