Skip to content

Commit

Permalink
feat(cat-voices): Proposal setup section (#1177)
Browse files Browse the repository at this point in the history
* chore: Adding needed translation

* feat: Adding models for guidance and comment

* feat: Adding widgets for guidance and comment

* feat: Guidance cubit for filtering guidance in the view

* test: Adding tests to VoicesDropdown Widget

* feat: reacting to changing state of proposal navigation

* feat: add new dictionary entry for ryszard-schossler and update guidance extension comments for clarity

* fix: empty list of guidance

* refactor: applying sugestion from code review

* chore: update .gitignore to exclude devtools_options.yaml and remove obsolete files from voices and uikit_example directories

* refactor: naming cleanup

* Delete catalyst_voices/packages/internal/catalyst_voices_localization/lib/generated/catalyst_voices_localizations.dart

* Delete catalyst_voices/packages/internal/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_en.dart

* Delete catalyst_voices/packages/internal/catalyst_voices_localization/lib/generated/catalyst_voices_localizations_es.dart

---------

Co-authored-by: Dominik Toton <[email protected]>
Co-authored-by: Damian Molinski <[email protected]>
Co-authored-by: Damian Moliński <[email protected]>
  • Loading branch information
4 people authored Nov 18, 2024
1 parent 31ad353 commit d7db9f8
Show file tree
Hide file tree
Showing 20 changed files with 709 additions and 15 deletions.
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ trailings
TXNZD
txos
Typer
ryszard-schossler
unawaited
unchunk
Unlogged
Expand Down
21 changes: 21 additions & 0 deletions catalyst_voices/apps/voices/lib/common/ext/guidance_ext.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_localization/generated/catalyst_voices_localizations.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';

extension GuidanceExt on GuidanceType {
String localizedType(VoicesLocalizations localizations) => switch (this) {
GuidanceType.mandatory => localizations.mandatoryGuidanceType,
GuidanceType.education => localizations.educationGuidanceType,
GuidanceType.tips => localizations.tipsGuidanceType,
};

// TODO(ryszard-schossler): when designers will
// provide us with icon, change here accordingly
SvgGenImage get icon {
return switch (this) {
GuidanceType.education => VoicesAssets.icons.newspaper,
GuidanceType.mandatory => VoicesAssets.icons.newspaper,
GuidanceType.tips => VoicesAssets.icons.newspaper,
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import 'package:catalyst_voices/common/ext/guidance_ext.dart';
import 'package:catalyst_voices/widgets/cards/guidance_card.dart';
import 'package:catalyst_voices/widgets/dropdown/voices_dropdown.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';

class GuidanceView extends StatefulWidget {
final List<Guidance> guidances;
const GuidanceView(this.guidances);

@override
State<GuidanceView> createState() => _GuidanceViewState();
}

class _GuidanceViewState extends State<GuidanceView> {
final List<Guidance> filteredGuidances = [];

GuidanceType? selectedType;

@override
void initState() {
super.initState();
filteredGuidances
..clear()
..addAll(widget.guidances);
}

@override
void didUpdateWidget(GuidanceView oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.guidances != widget.guidances) {
filteredGuidances
..clear()
..addAll(widget.guidances);
_filterGuidances(selectedType);
}
}

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
VoicesDropdown<GuidanceType?>(
items: GuidanceType.values
.map(
(e) => VoicesDropdownMenuEntry<GuidanceType>(
label: e.localizedType(context.l10n),
value: e,
context: context,
),
)
.toList(),
onChanged: (value) {
setState(() {
_filterGuidances(value);
});
},
value: selectedType,
),
if (filteredGuidances.isEmpty)
Center(
child: Text(context.l10n.noGuidanceOfThisType),
),
Column(
children: filteredGuidances
.sortedByWeight()
.toList()
.map((e) => GuidanceCard(guidance: e))
.toList(),
),
],
);
}

void _filterGuidances(GuidanceType? type) {
selectedType = type;
filteredGuidances
..clear()
..addAll(
type == null
? widget.guidances
: widget.guidances.where((e) => e.type == type).toList(),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@ import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';

const sections = [
ProposalSetup(
final sections = [
const ProposalSetup(
id: 0,
steps: [
TitleStep(
id: 0,
sectionId: 0,
data: DocumentJson(title),
guidances: mockGuidance,
),
],
),
Expand All @@ -34,24 +35,29 @@ const sections = [
ProblemStep(
id: 0,
sectionId: 1,
data: DocumentJson(problemStatement),
data: const DocumentJson(problemStatement),
charsLimit: 200,
guidances: [
mockGuidance[0],
],
),
SolutionStep(
const SolutionStep(
id: 1,
sectionId: 1,
data: DocumentJson(solutionStatement),
charsLimit: 200,
guidances: mockGuidance,
),
PublicDescriptionStep(
const PublicDescriptionStep(
id: 2,
sectionId: 1,
data: DocumentJson(publicDescription),
charsLimit: 3000,
guidances: mockGuidance,
),
],
),
ProposalSolution(
const ProposalSolution(
id: 2,
steps: [
ProblemPerspectiveStep(
Expand All @@ -74,7 +80,7 @@ const sections = [
),
],
),
ProposalImpact(
const ProposalImpact(
id: 3,
steps: [
BonusMarkUpStep(
Expand All @@ -91,7 +97,7 @@ const sections = [
),
],
),
CompatibilityAndFeasibility(
const CompatibilityAndFeasibility(
id: 4,
steps: [
DeliveryAndAccountabilityStep(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,40 @@
import 'package:catalyst_voices/pages/workspace/workspace_guidance_view.dart';
import 'package:catalyst_voices/widgets/cards/comment_card.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';

const List<Guidance> mockGuidance = [
Guidance(
title: 'Use a Compelling Hook or Unique Angle',
description:
'''Adding an element of intrigue or a unique approach can make your title stand out. For example, “Revolutionizing Urban Mobility with Eco-Friendly Innovation” not only describes the proposal but also piques curiosity.''',
type: GuidanceType.tips,
weight: 1,
),
Guidance(
title: 'Be Specific and Solution-Oriented',
description:
'''Use keywords that pinpoint the problem you’re solving or the opportunity you’re capitalizing on. A title like “Streamlining Supply Chains for Cost-Effective and Rapid Delivery” instantly tells the reader what the proposal aims to achieve.''',
type: GuidanceType.mandatory,
weight: 2,
),
Guidance(
title: 'Highlight the Benefit or Outcome',
description:
'''Make sure the reader can immediately see the value or the end result of your proposal. A title like “Boosting Engagement and Growth through Targeted Digital Strategies” puts the focus on the positive outcomes.''',
type: GuidanceType.mandatory,
weight: 1,
),
Guidance(
title: 'Education',
description: 'Use keywords that pinpoint the problem yo',
type: GuidanceType.education,
weight: 1,
),
];

class WorkspaceSetupPanel extends StatelessWidget {
const WorkspaceSetupPanel({super.key});

Expand All @@ -14,17 +47,54 @@ class WorkspaceSetupPanel extends StatelessWidget {
tabs: [
SpaceSidePanelTab(
name: 'Guidance',
body: const Offstage(),
body: SetupSectionListener(
SectionsControllerScope.of(context),
),
),
SpaceSidePanelTab(
name: 'Comments',
body: const Offstage(),
),
SpaceSidePanelTab(
name: 'Actions',
body: const Offstage(),
body: CommentCard(
comment: Comment(
text: 'Lacks clarity on key objectives and measurable outcomes.',
date: DateTime.now(),
userName: 'Community Member',
),
),
),
//No actions for now
// SpaceSidePanelTab(
// name: 'Actions',
// body: const Offstage(),
// ),
],
);
}
}

class SetupSectionListener extends StatelessWidget {
final SectionsController _controller;

const SetupSectionListener(
this._controller, {
super.key,
});

@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _controller,
builder: (context, value, _) {
final activeStepId = value.activeStepId;
final activeStepGuidances = value.activeStepGuidances;

if (activeStepId == null) {
return Text(context.l10n.selectASection);
} else if (activeStepGuidances == null || activeStepGuidances.isEmpty) {
return Text(context.l10n.noGuidanceForThisSection);
} else {
return GuidanceView(activeStepGuidances);
}
},
);
}
}
51 changes: 51 additions & 0 deletions catalyst_voices/apps/voices/lib/widgets/cards/comment_card.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import 'package:catalyst_voices/widgets/avatars/voices_avatar.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';

class CommentCard extends StatelessWidget {
final Comment comment;

const CommentCard({
super.key,
required this.comment,
});

@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
border: Border.all(
color: Theme.of(context).colorScheme.outline.withOpacity(.38),
width: 1,
),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VoicesAssets.icons.chatAlt.buildIcon(),
const SizedBox(height: 10),
Text(comment.text),
const SizedBox(height: 10),
Row(
children: [
VoicesAvatar(icon: VoicesAssets.icons.user.buildIcon()),
const SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(comment.userName),
Text(comment.date.toString()),
],
),
],
),
],
),
),
);
}
}
Loading

0 comments on commit d7db9f8

Please sign in to comment.