Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cat-voices): Proposal setup section #1177

Merged
merged 22 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8aeba29
chore: Adding needed translation
LynxLynxx Nov 8, 2024
259ab78
feat: Adding models for guidance and comment
LynxLynxx Nov 8, 2024
2746551
feat: Adding widgets for guidance and comment
LynxLynxx Nov 8, 2024
b36415c
feat: Guidance cubit for filtering guidance in the view
LynxLynxx Nov 8, 2024
263f57e
test: Adding tests to VoicesDropdown Widget
LynxLynxx Nov 8, 2024
1ad3c7b
Merge branch 'main' into feature/1176_proposal_section
LynxLynxx Nov 11, 2024
a9f2a7f
Merge branch 'main' into feature/1176_proposal_section
LynxLynxx Nov 13, 2024
021a74e
Merge branch 'main' into feature/1176_proposal_section
LynxLynxx Nov 13, 2024
f3b818d
feat: reacting to changing state of proposal navigation
LynxLynxx Nov 14, 2024
e2cf28b
Merge branch 'feature/1176_proposal_section' of github.com:input-outp…
LynxLynxx Nov 14, 2024
390ef9b
Merge branch 'main' into feature/1176_proposal_section
dtscalac Nov 14, 2024
33ae397
feat: add new dictionary entry for ryszard-schossler and update guida…
LynxLynxx Nov 14, 2024
6eacc4e
Merge branch 'feature/1176_proposal_section' of github.com:input-outp…
LynxLynxx Nov 14, 2024
c004c52
fix: empty list of guidance
LynxLynxx Nov 14, 2024
344ca2e
Merge branch 'main' into feature/1176_proposal_section
LynxLynxx Nov 14, 2024
6b7c307
refactor: applying sugestion from code review
LynxLynxx Nov 14, 2024
9491332
chore: update .gitignore to exclude devtools_options.yaml and remove …
LynxLynxx Nov 15, 2024
913d1bd
Merge main into feature/1176_proposal_section
damian-molinski Nov 18, 2024
3c53a61
refactor: naming cleanup
damian-molinski Nov 18, 2024
76dc552
Delete catalyst_voices/packages/internal/catalyst_voices_localization…
damian-molinski Nov 18, 2024
21e2471
Delete catalyst_voices/packages/internal/catalyst_voices_localization…
damian-molinski Nov 18, 2024
73f6e4f
Delete catalyst_voices/packages/internal/catalyst_voices_localization…
damian-molinski Nov 18, 2024
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
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(
LynxLynxx marked this conversation as resolved.
Show resolved Hide resolved
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);
}
LynxLynxx marked this conversation as resolved.
Show resolved Hide resolved
},
);
}
}
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({
LynxLynxx marked this conversation as resolved.
Show resolved Hide resolved
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
Loading