Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.
Open
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
14 changes: 10 additions & 4 deletions lib/app_router.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import 'package:flutter/material.dart';
import 'package:lb_planner/features/feedback/presentation/screens/feedback_page.dart';
import 'package:lb_planner/features/themes/themes.dart';
import 'package:lb_planner/shared/shared.dart';
import 'package:auto_route/auto_route.dart';
import 'package:lb_planner/features/auth/auth.dart';
import 'package:lb_planner/features/feedback/presentation/screens/feedback.dart';

part 'app_router.gr.dart';

Expand Down Expand Up @@ -109,15 +112,18 @@ class AppRouter extends _$AppRouter {
path: '/theme-development',
),
DefaultRoute(
page: LoginRoute.page,
path: '/login',
)
page: AdminFeedbacksRoute.page,
path: '/feedback',
initial: true,
title: (context, data) => context.t.admin_feedback_routeName,
),
];
}

/// Implements [CustomRoute] with some default settings.
class DefaultRoute extends CustomRoute {
/// Implements [CustomRoute] with some default settings.
DefaultRoute({required super.page, required super.path, super.initial})
DefaultRoute(
{required super.page, required super.path, super.initial, super.title})
: super(transitionsBuilder: TransitionsBuilders.noTransition);
}
68 changes: 68 additions & 0 deletions lib/app_router.gr.dart

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

3 changes: 3 additions & 0 deletions lib/features/auth/domain/models/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ class User with _$User {
bool hasCapability(UserCapability capability) =>
capabilities.contains(capability);

/// Returns the full name of the user.
String get fullname => '$firstname $lastname';

/// Returns `true` if this user has elevated privileges (i.e. [UserCapability.dev] or [UserCapability.moderator]). Otherwise `false`.
bool get isElevated =>
hasCapability(UserCapability.dev) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,17 @@ class FeedbackProviderState extends AutoRefreshAsyncNotifier<List<Feedback>> {
return true;
}).toList();
}

/// Returns the [Feedback] with the given [id].
///
/// If no feedback with the specified [id] can be found or [state.hasValue] is `false` this method will throw a [StateError].
Feedback getFeedbackById(int id) {
if (!state.hasValue) throw StateError("State is ${state.runtimeType}!");

final feedback = state.requireValue.firstWhereOrNull((e) => e.id == id);

if (feedback == null) throw StateError("Feedback with id $id not found!");

return feedback;
}
}
2 changes: 2 additions & 0 deletions lib/features/feedback/presentation/presentation.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'widgets/widgets.dart';
export 'screens/screens.dart';
156 changes: 156 additions & 0 deletions lib/features/feedback/presentation/screens/feedback.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import 'package:flutter/material.dart' hide Feedback;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:lb_planner/features/feedback/presentation/presentation.dart';
import 'package:lb_planner/shared/shared.dart';
import 'package:lb_planner/features/feedback/domain/domain.dart';
import 'package:lb_planner/features/feedback/presentation/widgets/widgets.dart';

@RoutePage()

/// Shows all feedbacks for the admin.
class AdminFeedbacksScreen extends StatefulWidget {
/// Shows all feedbacks for the admin.
const AdminFeedbacksScreen({Key? key}) : super(key: key);

/// The font size of the header.
static const double headerFontSize = 20;

@override
State<AdminFeedbacksScreen> createState() => _AdminFeedbacksScreenState();

/// Sorts the given feedback list
static List<Feedback>? sortFeedbacks(AsyncValue<List<Feedback>> feedbacks) {
feedbacks.value?.sort(
(a, b) {
var status = a.readAsInt.compareTo(b.readAsInt);

if (status != 0) return status;

var timestamp = b.createdAt.compareTo(a.createdAt);

return timestamp;
},
);

return feedbacks.value;
}
}

class _AdminFeedbacksScreenState extends State<AdminFeedbacksScreen> {
@override
Widget build(BuildContext context) {
return Material(
child: Sidebar(
body: Consumer(builder: (context, ref, _) {
var feedbacks = ref.watch(feedbackProvider);

var sortedFeedbacks = AdminFeedbacksScreen.sortFeedbacks(feedbacks);

return ConditionalWidget(
ifFalse: Center(
child: Text(
t.admin_feedback_noFeedback,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: AdminFeedbacksScreen.headerFontSize,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.left,
),
),
condition: sortedFeedbacks?.isNotEmpty ?? false,
ifTrue: Align(
alignment: Alignment.topLeft,
child: Column(
children: [
Row(
children: [
Expanded(
child: Text(
t.admin_feedback_headers_user,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: AdminFeedbacksScreen.headerFontSize,
fontWeight: FontWeight.w600,
),
),
),
Expanded(
child: Text(
t.admin_feedback_headers_status,
textAlign: TextAlign.center,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: AdminFeedbacksScreen.headerFontSize,
fontWeight: FontWeight.w600,
),
),
),
Expanded(
child: Text(
t.admin_feedback_headers_type,
textAlign: TextAlign.center,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: AdminFeedbacksScreen.headerFontSize,
fontWeight: FontWeight.w600,
),
),
),
Expanded(
child: Text(
t.admin_feedback_headers_lastModified,
textAlign: TextAlign.center,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: AdminFeedbacksScreen.headerFontSize,
fontWeight: FontWeight.w600,
),
),
),
Expanded(
child: Text(
t.admin_feedback_headers_lastModifiedBy,
textAlign: TextAlign.center,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: AdminFeedbacksScreen.headerFontSize,
fontWeight: FontWeight.w600,
),
),
),
Expanded(
child: Text(
t.admin_feedback_headers_timestamp,
textAlign: TextAlign.center,
style: TextStyle(
overflow: TextOverflow.ellipsis,
fontSize: AdminFeedbacksScreen.headerFontSize,
fontWeight: FontWeight.w600,
),
),
),
],
),
Spacing.large(),
Expanded(
child: ListView(
controller: ScrollController(),
children: [
if (sortedFeedbacks != null)
for (var feedback in sortedFeedbacks) ...[
AdminFeedbackItem(feedbackId: feedback.id),
Spacing.medium(),
],
],
),
),
],
),
),
);
}),
),
);
}
}
Loading