Skip to content

✨(global) add customization to feedbacks #248

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

Merged
merged 3 commits into from
Jul 3, 2025
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ and this project adheres to

- ✨(front) add move modal #213
- ✨(front) update the homepage to alpha #234
- ✨(global) add customization to feedbacks

## Changed

Expand Down
7 changes: 7 additions & 0 deletions env.d/development/common.dist
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,10 @@ LOGOUT_REDIRECT_URL=http://localhost:3000

OIDC_REDIRECT_ALLOWED_HOSTS="http://localhost:8083,http://localhost:3000"
OIDC_AUTH_REQUEST_EXTRA_PARAMS={"acr_values": "eidas1"}

# Frontend
FRONTEND_THEME=dsfr
FRONTEND_MORE_LINK=https://docs.numerique.gouv.fr/docs/fa0aba15-e119-4185-b466-a4b37ad95950/
FRONTEND_FEEDBACK_BUTTON_SHOW=True
FRONTEND_FEEDBACK_BUTTON_IDLE=False
FRONTEND_FEEDBACK_ITEMS={"form":{"url": "https://tally.so/r/w2YaWL"}, "tchap":{"url": "https://tchap.gouv.fr/#/room/#fichiers:agent.dinum.tchap.gouv.fr"}, "visio":{"url": "https://cal.com/robin-lecomte-eyul9w/fichiers"}}
4 changes: 4 additions & 0 deletions src/backend/core/api/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -1194,6 +1194,10 @@ def get(self, request):
"CRISP_WEBSITE_ID",
"ENVIRONMENT",
"FRONTEND_THEME",
"FRONTEND_MORE_LINK",
"FRONTEND_FEEDBACK_BUTTON_SHOW",
"FRONTEND_FEEDBACK_BUTTON_IDLE",
"FRONTEND_FEEDBACK_ITEMS",
"MEDIA_BASE_URL",
"POSTHOG_KEY",
"POSTHOG_HOST",
Expand Down
8 changes: 8 additions & 0 deletions src/backend/core/tests/test_api_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
@override_settings(
CRISP_WEBSITE_ID="123",
FRONTEND_THEME="test-theme",
FRONTEND_MORE_LINK="https://test.com",
FRONTEND_FEEDBACK_BUTTON_SHOW=True,
FRONTEND_FEEDBACK_BUTTON_IDLE=False,
FRONTEND_FEEDBACK_ITEMS={"form": {"url": "https://test.com"}},
MEDIA_BASE_URL="http://testserver/",
POSTHOG_KEY="132456",
POSTHOG_HOST="https://eu.i.posthog-test.com",
Expand All @@ -38,6 +42,10 @@ def test_api_config(is_authenticated):
"CRISP_WEBSITE_ID": "123",
"ENVIRONMENT": "test",
"FRONTEND_THEME": "test-theme",
"FRONTEND_MORE_LINK": "https://test.com",
"FRONTEND_FEEDBACK_BUTTON_SHOW": True,
"FRONTEND_FEEDBACK_BUTTON_IDLE": False,
"FRONTEND_FEEDBACK_ITEMS": {"form": {"url": "https://test.com"}},
"LANGUAGES": [["en-us", "English"], ["fr-fr", "French"], ["de-de", "German"]],
"LANGUAGE_CODE": "en-us",
"MEDIA_BASE_URL": "http://testserver/",
Expand Down
17 changes: 16 additions & 1 deletion src/backend/drive/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,22 @@ class Base(Configuration):
FRONTEND_THEME = values.Value(
None, environ_name="FRONTEND_THEME", environ_prefix=None
)

FRONTEND_MORE_LINK = values.Value(
None,
environ_name="FRONTEND_MORE_LINK",
environ_prefix=None,
)
FRONTEND_FEEDBACK_BUTTON_SHOW = values.BooleanValue(
default=False, environ_name="FRONTEND_FEEDBACK_BUTTON_SHOW", environ_prefix=None
)
# For instance, you might want to bind this button to an external library like
# Posthog to trigger survey instead of the build in feedback modal.
FRONTEND_FEEDBACK_BUTTON_IDLE = values.BooleanValue(
default=False, environ_name="FRONTEND_FEEDBACK_BUTTON_IDLE", environ_prefix=None
)
FRONTEND_FEEDBACK_ITEMS = values.DictValue(
{}, environ_name="FRONTEND_FEEDBACK_ITEMS", environ_prefix=None
)
# Crisp
CRISP_WEBSITE_ID = values.Value(
None, environ_name="CRISP_WEBSITE_ID", environ_prefix=None
Expand Down
6 changes: 1 addition & 5 deletions src/frontend/apps/drive/.env
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
NEXT_PUBLIC_S3_DOMAIN_REPLACE=
NEXT_PUBLIC_API_ORIGIN=
NEXT_PUBLIC_ALPHA_MORE=https://docs.numerique.gouv.fr/docs/fa0aba15-e119-4185-b466-a4b37ad95950/
NEXT_PUBLIC_ALPHA_FEEDBACK_FORM=https://tally.so/r/w2YaWL
NEXT_PUBLIC_ALPHA_FEEDBACK_TCHAP="https://tchap.gouv.fr/#/room/#fichiers:agent.dinum.tchap.gouv.fr"
NEXT_PUBLIC_ALPHA_FEEDBACK_VISIO=https://cal.com/robin-lecomte-eyul9w/fichiers
NEXT_PUBLIC_API_ORIGIN=
4 changes: 0 additions & 4 deletions src/frontend/apps/drive/.env.development
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
NEXT_PUBLIC_S3_DOMAIN_REPLACE=http://localhost:9000
NEXT_PUBLIC_API_ORIGIN=http://localhost:8071
NEXT_PUBLIC_ALPHA_MORE=https://docs.numerique.gouv.fr/docs/fa0aba15-e119-4185-b466-a4b37ad95950/
NEXT_PUBLIC_ALPHA_FEEDBACK_FORM=https://tally.so/r/w2YaWL
NEXT_PUBLIC_ALPHA_FEEDBACK_TCHAP="https://tchap.gouv.fr/#/room/#fichiers:agent.dinum.tchap.gouv.fr"
NEXT_PUBLIC_ALPHA_FEEDBACK_VISIO=https://cal.com/robin-lecomte-eyul9w/fichiers
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export function useApiConfig() {
return useQuery({
queryKey: ["config"],
queryFn: () => driver.getConfig(),
staleTime: 1000,
});
}
4 changes: 4 additions & 0 deletions src/frontend/apps/drive/src/features/drivers/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ export type User = {
export type ApiConfig = {
POSTHOG_KEY?: string;
POSTHOG_HOST?: string;
FRONTEND_MORE_LINK?: string;
FRONTEND_FEEDBACK_BUTTON_SHOW?: boolean;
FRONTEND_FEEDBACK_BUTTON_IDLE?: boolean;
FRONTEND_FEEDBACK_ITEMS?: Record<string, { url: string }>;
};

export interface APIList<T> {
Expand Down
83 changes: 58 additions & 25 deletions src/frontend/apps/drive/src/features/feedback/Feedback.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,75 @@ import {
} from "@openfun/cunningham-react";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useApiConfig } from "../config/useApiConfig";

export const Feedback = (props: { buttonProps?: Partial<ButtonProps> }) => {
const { t } = useTranslation();
const modal = useModal();
const { data: config } = useApiConfig();

const FEEDBACK_BUTTONS = useMemo(
() => [
{
icon: <FormIcon />,
title: "feedback.modal.buttons.form.title",
description: "feedback.modal.buttons.form.description",
href: process.env.NEXT_PUBLIC_ALPHA_FEEDBACK_FORM,
},
{
icon: <TchapIcon />,
title: "feedback.modal.buttons.tchap.title",
description: "feedback.modal.buttons.tchap.description",
href: process.env.NEXT_PUBLIC_ALPHA_FEEDBACK_TCHAP,
},
{
icon: <VisioIcon />,
title: "feedback.modal.buttons.visio.title",
description: "feedback.modal.buttons.visio.description",
href: process.env.NEXT_PUBLIC_ALPHA_FEEDBACK_VISIO,
},
],
[]
);
const FEEDBACK_BUTTONS = useMemo(() => {
return config?.FRONTEND_FEEDBACK_ITEMS
? Object.entries(config.FRONTEND_FEEDBACK_ITEMS).map(([key, value]) => {
let Icon: React.ReactElement;
switch (key) {
case "form":
Icon = <FormIcon />;
break;
case "tchap":
Icon = <TchapIcon />;
break;
case "visio":
Icon = <VisioIcon />;
break;
default:
Icon = <FormIcon />;
break;
}
return {
icon: Icon,
title: `feedback.modal.buttons.${key}.title`,
description: `feedback.modal.buttons.${key}.description`,
href: value.url,
};
})
: [];
}, []);

const showFeedbackButton = () => {
if (!config?.FRONTEND_FEEDBACK_BUTTON_SHOW) {
return false;
}

// For idle mode, there is no feedback buttons displayed as the modal will never show up,
// so we show the button even if there is no href.
if (
!config?.FRONTEND_FEEDBACK_BUTTON_IDLE &&
FEEDBACK_BUTTONS.filter((button) => !!button.href).length === 0
) {
return false;
}
return true;
};

const onClick = () => {
if (config?.FRONTEND_FEEDBACK_BUTTON_IDLE) {
return;
}
modal.open();
};

if (!showFeedbackButton()) {
return null;
}

return (
<>
<Button
color="tertiary"
icon={<Icon name="info" />}
className="c__feedback__button"
onClick={modal.open}
onClick={onClick}
{...props.buttonProps}
>
{t("feedback.button")}
Expand All @@ -57,7 +90,7 @@ export const Feedback = (props: { buttonProps?: Partial<ButtonProps> }) => {
{t("feedback.modal.description")}
</p>
<div className="c__feedback__modal__buttons">
{FEEDBACK_BUTTONS.map((button) => (
{FEEDBACK_BUTTONS.filter((button) => !!button.href).map((button) => (
<FeedbackButton
key={button.title}
{...button}
Expand Down
4 changes: 3 additions & 1 deletion src/frontend/apps/drive/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ import {
} from "@/features/ui/components/toaster/Toaster";
import { Button } from "@openfun/cunningham-react";
import { Feedback } from "@/features/feedback/Feedback";
import { useApiConfig } from "@/features/config/useApiConfig";
export default function HomePage() {
const { t } = useTranslation();
const { user } = useAuth();
const { data: config } = useApiConfig();

useEffect(() => {
if (user) {
Expand Down Expand Up @@ -88,7 +90,7 @@ export default function HomePage() {
color="secondary"
icon={<Icon name="info" type={IconType.OUTLINED} />}
fullWidth
href={process.env.NEXT_PUBLIC_ALPHA_MORE}
href={config?.FRONTEND_MORE_LINK}
target="_blank"
>
{t("home.more")}
Expand Down
Loading