167 lines
5.8 KiB
Dart
167 lines
5.8 KiB
Dart
import 'package:flutter/cupertino.dart';
|
|
import 'package:please_pay_me/core/format/formatters.dart';
|
|
import 'package:please_pay_me/data/models/job.dart';
|
|
import 'package:please_pay_me/features/work/job_form_sheet.dart';
|
|
import 'package:please_pay_me/features/work/jobs_controller.dart';
|
|
import 'package:please_pay_me/theme/theme.dart';
|
|
import 'package:please_pay_me/ui/ui.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
/// Jobs and their payday schedule.
|
|
class WorkScreen extends StatelessWidget {
|
|
const WorkScreen({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final controller = context.watch<JobsController>();
|
|
|
|
return CupertinoPageScaffold(
|
|
backgroundColor: AppColors.of(context, AppColors.groupedBackground),
|
|
child: CustomScrollView(
|
|
physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()),
|
|
slivers: [
|
|
AppLargeNavBar(
|
|
title: 'Работа',
|
|
trailing: CupertinoButton(
|
|
padding: EdgeInsets.zero,
|
|
minimumSize: Size.zero,
|
|
onPressed: () => showJobFormSheet(context: context, controller: controller),
|
|
child: const AppIcon(CupertinoIcons.add_circled, color: AppColors.accent),
|
|
),
|
|
),
|
|
CupertinoSliverRefreshControl(onRefresh: () => controller.load(silent: true)),
|
|
SliverToBoxAdapter(
|
|
child: controller.state.map(
|
|
loading: () => AppListSection(
|
|
children: List.generate(2, (_) => const AppSkeletonRow()),
|
|
),
|
|
error: (message) => AppErrorView(message: message, onRetry: controller.load),
|
|
data: (jobs) => jobs.isEmpty
|
|
? AppEmptyState(
|
|
icon: CupertinoIcons.briefcase,
|
|
title: 'Работа не добавлена',
|
|
message: 'Укажите оклад и дни выплат — приложение подскажет даты зарплаты.',
|
|
actionLabel: 'Добавить работу',
|
|
onAction: () =>
|
|
showJobFormSheet(context: context, controller: controller),
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
for (final job in jobs) ...[
|
|
_JobCard(job: job, controller: controller),
|
|
const SizedBox(height: AppSpacing.s5),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
),
|
|
const SliverToBoxAdapter(child: SizedBox(height: AppSpacing.s7)),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _JobCard extends StatelessWidget {
|
|
const _JobCard({required this.job, required this.controller});
|
|
|
|
final Job job;
|
|
final JobsController controller;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
AppListSection(
|
|
header: job.name,
|
|
separatorIndent: 60,
|
|
children: [
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.briefcase_fill,
|
|
color: AppColors.accent,
|
|
),
|
|
title: 'Оклад',
|
|
value: formatMoney(job.salaryAmount, currency: job.currency),
|
|
showChevron: false,
|
|
),
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.calendar,
|
|
color: AppColors.systemOrange,
|
|
),
|
|
title: 'Дни выплат',
|
|
value: job.payDays.map((day) => '$day').join(' и '),
|
|
showChevron: false,
|
|
),
|
|
AppListTile(
|
|
leading: const AppIconBadge(
|
|
icon: CupertinoIcons.arrow_left_right,
|
|
color: AppColors.systemGray,
|
|
),
|
|
title: 'Выходные',
|
|
value: job.weekendPolicy.label,
|
|
showChevron: false,
|
|
),
|
|
AppListTile(
|
|
title: 'Настроить',
|
|
onTap: () => _openActions(context),
|
|
),
|
|
],
|
|
),
|
|
if (job.nextPays.isNotEmpty) ...[
|
|
const SizedBox(height: AppSpacing.s4),
|
|
AppListSection(
|
|
header: 'Ближайшие выплаты',
|
|
children: [
|
|
for (final pay in job.nextPays)
|
|
AppListTile(
|
|
title: formatDay(pay.date),
|
|
subtitle: '${pay.percent.round()}% оклада · ${pay.scheduledDay} числа',
|
|
value: formatMoney(pay.amount, currency: job.currency),
|
|
showChevron: false,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Future<void> _openActions(BuildContext context) async {
|
|
final index = await showAppActionSheet(
|
|
context: context,
|
|
title: job.name,
|
|
actions: const [
|
|
AppActionSheetAction(label: 'Редактировать', isDefault: true),
|
|
AppActionSheetAction(label: 'Удалить', destructive: true),
|
|
],
|
|
);
|
|
|
|
if (index == null || !context.mounted) return;
|
|
|
|
if (index == 0) {
|
|
await showJobFormSheet(context: context, controller: controller, initial: job);
|
|
return;
|
|
}
|
|
|
|
final confirmed = await showAppAlert(
|
|
context: context,
|
|
title: 'Удалить «${job.name}»?',
|
|
message: 'График выплат тоже будет удалён.',
|
|
confirmLabel: 'Удалить',
|
|
cancelLabel: 'Отмена',
|
|
destructive: true,
|
|
);
|
|
|
|
if (confirmed != true) return;
|
|
|
|
final error = await controller.delete(job.id);
|
|
if (context.mounted) {
|
|
await showAppToast(context, message: error ?? 'Работа удалена');
|
|
}
|
|
}
|
|
}
|