feat: enhance feedback records functionality with refresh feature

- Added a new refresh mechanism for feedback records in the FeedbackRecordsTable component, including loading states and error handling.
- Updated translations for feedback records refresh messages in multiple languages.
- Introduced new checksums in the i18n.lock file to reflect the changes.
This commit is contained in:
Johannes
2026-03-06 10:39:46 +01:00
parent 455fdd7722
commit 54f2b58732
16 changed files with 86 additions and 31 deletions

View File

@@ -9,6 +9,7 @@ import {
TypeIcon,
} from "lucide-react";
import { useCallback, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { listFeedbackRecordsAction } from "@/lib/connector/actions";
import { getFormattedErrorMessage } from "@/lib/utils/helper";
@@ -102,8 +103,32 @@ export function FeedbackRecordsTable({
fetchRecords(records.length, true);
};
const handleRefresh = () => {
fetchRecords(0, false);
const handleRefresh = async () => {
if (isRefreshing) return;
setIsRefreshing(true);
setError(null);
const toastId = toast.loading(t("environments.unify.refreshing_feedback_records"));
const result = await listFeedbackRecordsAction({
environmentId,
limit: RECORDS_PER_PAGE,
offset: 0,
});
if (!result?.data) {
toast.error(
getFormattedErrorMessage(result) ?? t("environments.unify.failed_to_load_feedback_records"),
{ id: toastId }
);
setIsRefreshing(false);
return;
}
setRecords(result.data.data);
setTotal(result.data.total);
setIsRefreshing(false);
toast.success(t("environments.unify.feedback_records_refreshed"), { id: toastId });
};
const hasMore = records.length < total;
@@ -122,38 +147,26 @@ export function FeedbackRecordsTable({
);
}
if (records.length === 0 && !isRefreshing) {
return (
<div className="rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="flex h-48 flex-col items-center justify-center gap-2 px-4 text-center">
<MessageSquareTextIcon className="h-8 w-8 text-slate-400" />
<p className="text-sm text-slate-500">{t("environments.unify.no_feedback_records")}</p>
</div>
</div>
);
}
const isEmpty = records.length === 0 && !isRefreshing;
return (
<div className="space-y-3">
<div className="flex items-center justify-between">
<p className="text-sm text-slate-500">
{t("environments.unify.showing_count", { count: records.length, total })}
</p>
<Button
variant="secondary"
size="sm"
onClick={handleRefresh}
loading={isRefreshing}
className="gap-1.5">
<RefreshCwIcon className="h-3.5 w-3.5" />
</Button>
</div>
{!isEmpty && (
<div className="flex items-center justify-between">
<p className="text-sm text-slate-500">
{t("environments.unify.showing_count", { count: records.length, total })}
</p>
<Button variant="secondary" size="sm" onClick={handleRefresh} disabled={isRefreshing}>
<RefreshCwIcon className="h-3.5 w-3.5" />
</Button>
</div>
)}
<div className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
<div className="overflow-x-auto">
<table className="w-full min-w-[900px]">
<thead>
<tr className="border-b border-slate-200 text-left text-sm font-semibold text-slate-900">
<tr className="border-b border-slate-200 text-left text-sm text-slate-900 [&>th]:font-semibold">
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.collected_at")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.source_type")}</th>
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.source_name")}</th>
@@ -163,11 +176,23 @@ export function FeedbackRecordsTable({
<th className="whitespace-nowrap px-4 py-3">{t("environments.unify.user_identifier")}</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{records.map((record) => (
<FeedbackRecordRow key={record.id} record={record} locale={i18n.language} />
))}
</tbody>
{isEmpty ? (
<tbody>
<tr>
<td colSpan={7}>
<div className="flex h-32 items-center justify-center">
<p className="text-sm text-slate-500">{t("environments.unify.no_feedback_records")}</p>
</div>
</td>
</tr>
</tbody>
) : (
<tbody className="divide-y divide-slate-100">
{records.map((record) => (
<FeedbackRecordRow key={record.id} record={record} locale={i18n.language} />
))}
</tbody>
)}
</table>
</div>
</div>

View File

@@ -1975,6 +1975,7 @@ checksums:
environments/unify/feedback_date: ddba5d3270d4a6394d29721025a04400
environments/unify/feedback_record_fields: 88c0f13afeb88fe751f85e79b0f73064
environments/unify/feedback_records: e24cf48bb6985910f4ffe5e00512d388
environments/unify/feedback_records_refreshed: 4b27a8e2a8dbe8afa945d9f874aa7ef1
environments/unify/field_label: 6384505ca0e40010c666b712511132a6
environments/unify/field_type: 2581066dc304c853a4a817c20996fa08
environments/unify/formbricks_surveys: eba2fce04ee68f02626e5509adf7d66a
@@ -1998,6 +1999,7 @@ checksums:
environments/unify/question_selected: b9ff13b6212874258da911867932dc7d
environments/unify/question_type_not_supported: 8d9f7554e3b509dfd5307d8d1fef08d7
environments/unify/questions_selected: 1f13d6fecafa2ce5ea9e6d07078a1d38
environments/unify/refreshing_feedback_records: 2a03b44510ebe19eea6473639e9a7222
environments/unify/required: 04d7fb6f37ffe0a6ca97d49e2a8b6eb5
environments/unify/save_changes: 53dd9f4f0a4accc822fa5c1f2f6d118a
environments/unify/select_a_survey_to_see_questions: 792eba3d2f6d210231a2266401111a20

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Aktuelles Datum",
"feedback_record_fields": "Feedback-Datensatzfelder",
"feedback_records": "Feedback-Einträge",
"feedback_records_refreshed": "Feedback-Einträge aktualisiert",
"field_label": "Feldbezeichnung",
"field_type": "Feldtyp",
"formbricks_surveys": "Formbricks Umfragen",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> Frage ausgewählt. Jede Antwort auf diese Frage erstellt einen neuen Feedback-Datensatz.",
"question_type_not_supported": "Dieser Fragetyp wird nicht unterstützt",
"questions_selected": "<strong>{count}</strong> Fragen ausgewählt. Jede Antwort auf diese Fragen erstellt einen neuen Feedback-Datensatz.",
"refreshing_feedback_records": "Feedback-Einträge werden aktualisiert...",
"required": "Erforderlich",
"save_changes": "Änderungen speichern",
"select_a_survey_to_see_questions": "Wähle eine Umfrage aus, um ihre Fragen zu sehen",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Current date",
"feedback_record_fields": "Feedback Record Fields",
"feedback_records": "Feedback Records",
"feedback_records_refreshed": "Feedback records refreshed",
"field_label": "Field Label",
"field_type": "Field Type",
"formbricks_surveys": "Formbricks Surveys",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> question selected. Each response to these questions will create a new Feedback Record.",
"question_type_not_supported": "This question type is not supported",
"questions_selected": "<strong>{count}</strong> questions selected. Each response to these questions will create a new Feedback Record.",
"refreshing_feedback_records": "Refreshing feedback records...",
"required": "Required",
"save_changes": "Save changes",
"select_a_survey_to_see_questions": "Select a survey to see its questions",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Fecha actual",
"feedback_record_fields": "Campos de registro de comentarios",
"feedback_records": "Registros de comentarios",
"feedback_records_refreshed": "Registros de comentarios actualizados",
"field_label": "Etiqueta de campo",
"field_type": "Tipo de campo",
"formbricks_surveys": "Formbricks Surveys",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> pregunta seleccionada. Cada respuesta a esta pregunta creará un registro de feedback nuevo.",
"question_type_not_supported": "Este tipo de pregunta no es compatible",
"questions_selected": "<strong>{count}</strong> preguntas seleccionadas. Cada respuesta a estas preguntas creará un registro de feedback nuevo.",
"refreshing_feedback_records": "Actualizando registros de comentarios...",
"required": "Obligatorio",
"save_changes": "Guardar cambios",
"select_a_survey_to_see_questions": "Selecciona una encuesta para ver sus preguntas",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Date actuelle",
"feedback_record_fields": "Champs d'enregistrement de feedback",
"feedback_records": "Enregistrements de feedback",
"feedback_records_refreshed": "Enregistrements de feedback actualisés",
"field_label": "Libellé du champ",
"field_type": "Type de champ",
"formbricks_surveys": "Sondages Formbricks",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> question sélectionnée. Chaque réponse à cette question créera un nouvel enregistrement de feedback.",
"question_type_not_supported": "Ce type de question n'est pas pris en charge",
"questions_selected": "<strong>{count}</strong> questions sélectionnées. Chaque réponse à ces questions créera un nouvel enregistrement de feedback.",
"refreshing_feedback_records": "Actualisation des enregistrements de feedback...",
"required": "Requis",
"save_changes": "Enregistrer les modifications",
"select_a_survey_to_see_questions": "Sélectionnez une enquête pour voir ses questions",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Aktuális dátum",
"feedback_record_fields": "Visszajelzési rekord mezők",
"feedback_records": "Visszajelzési rekordok",
"feedback_records_refreshed": "Visszajelzési rekordok frissítve",
"field_label": "Mező címke",
"field_type": "Mező típus",
"formbricks_surveys": "Formbricks kérdőívek",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> kérdés kiválasztva. Minden válasz ezekre a kérdésekre új visszajelzési rekordot hoz létre.",
"question_type_not_supported": "Ez a kérdéstípus nem támogatott",
"questions_selected": "<strong>{count}</strong> kérdés kiválasztva. Minden válasz ezekre a kérdésekre új visszajelzési rekordot hoz létre.",
"refreshing_feedback_records": "Visszajelzési rekordok frissítése...",
"required": "Kötelező",
"save_changes": "Változtatások mentése",
"select_a_survey_to_see_questions": "Válassz egy kérdőívet a kérdések megtekintéséhez",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "現在の日付",
"feedback_record_fields": "フィードバックレコードフィールド",
"feedback_records": "フィードバックレコード",
"feedback_records_refreshed": "フィードバックレコードを更新しました",
"field_label": "フィールドラベル",
"field_type": "フィールドタイプ",
"formbricks_surveys": "Formbricks フォーム",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong>件の質問が選択されています。これらの質問への各回答は、新しいフィードバックレコードを作成します。",
"question_type_not_supported": "この質問タイプはサポートされていません",
"questions_selected": "<strong>{count}</strong>件の質問が選択されています。これらの質問への各回答は、新しいフィードバックレコードを作成します。",
"refreshing_feedback_records": "フィードバックレコードを更新中...",
"required": "必須",
"save_changes": "変更を保存",
"select_a_survey_to_see_questions": "フォームを選択して質問を表示",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Huidige datum",
"feedback_record_fields": "Feedbackrecordvelden",
"feedback_records": "Feedbackrecords",
"feedback_records_refreshed": "Feedbackrecords vernieuwd",
"field_label": "Veldlabel",
"field_type": "Veldtype",
"formbricks_surveys": "Formbricks Surveys",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> vraag geselecteerd. Elk antwoord op deze vraag zal een nieuw feedbackrecord aanmaken.",
"question_type_not_supported": "Dit vraagtype wordt niet ondersteund",
"questions_selected": "<strong>{count}</strong> vragen geselecteerd. Elk antwoord op deze vragen zal een nieuw feedbackrecord aanmaken.",
"refreshing_feedback_records": "Feedbackrecords vernieuwen...",
"required": "Vereist",
"save_changes": "Wijzigingen opslaan",
"select_a_survey_to_see_questions": "Selecteer een enquête om de vragen te zien",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Data atual",
"feedback_record_fields": "Campos do registro de feedback",
"feedback_records": "Registros de feedback",
"feedback_records_refreshed": "Registros de feedback atualizados",
"field_label": "Rótulo do campo",
"field_type": "Tipo de campo",
"formbricks_surveys": "Pesquisas Formbricks",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> pergunta selecionada. Cada resposta a esta pergunta criará um novo registro de feedback.",
"question_type_not_supported": "Este tipo de pergunta não é suportado",
"questions_selected": "<strong>{count}</strong> perguntas selecionadas. Cada resposta a estas perguntas criará um novo registro de feedback.",
"refreshing_feedback_records": "Atualizando registros de feedback...",
"required": "Obrigatório",
"save_changes": "Salvar alterações",
"select_a_survey_to_see_questions": "Selecione uma pesquisa para ver suas perguntas",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Data atual",
"feedback_record_fields": "Campos de registo de feedback",
"feedback_records": "Registos de feedback",
"feedback_records_refreshed": "Registos de feedback atualizados",
"field_label": "Etiqueta do campo",
"field_type": "Tipo de campo",
"formbricks_surveys": "Pesquisas Formbricks",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> pergunta selecionada. Cada resposta a esta pergunta criará um novo registo de feedback.",
"question_type_not_supported": "Este tipo de pergunta não é suportado",
"questions_selected": "<strong>{count}</strong> perguntas selecionadas. Cada resposta a estas perguntas criará um novo registo de feedback.",
"refreshing_feedback_records": "A atualizar registos de feedback...",
"required": "Obrigatório",
"save_changes": "Guardar alterações",
"select_a_survey_to_see_questions": "Selecione um inquérito para ver as suas perguntas",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Data curentă",
"feedback_record_fields": "Câmpuri înregistrare feedback",
"feedback_records": "Înregistrări de feedback",
"feedback_records_refreshed": "Înregistrările de feedback au fost actualizate",
"field_label": "Etichetă câmp",
"field_type": "Tip câmp",
"formbricks_surveys": "Chestionare Formbricks",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> întrebare selectată. Fiecare răspuns la aceste întrebări va crea un nou Feedback Record.",
"question_type_not_supported": "Acest tip de întrebare nu este suportat",
"questions_selected": "<strong>{count}</strong> întrebări selectate. Fiecare răspuns la aceste întrebări va crea un nou Feedback Record.",
"refreshing_feedback_records": "Se actualizează înregistrările de feedback...",
"required": "Obligatoriu",
"save_changes": "Salvează modificările",
"select_a_survey_to_see_questions": "Selectează un chestionar pentru a vedea întrebările",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Текущая дата",
"feedback_record_fields": "Поля записи отзыва",
"feedback_records": "Записи отзывов",
"feedback_records_refreshed": "Записи отзывов обновлены",
"field_label": "Метка поля",
"field_type": "Тип поля",
"formbricks_surveys": "Formbricks Surveys",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> выбранный вопрос. Каждый ответ на эти вопросы создаст новую запись обратной связи.",
"question_type_not_supported": "Этот тип вопроса не поддерживается",
"questions_selected": "<strong>{count}</strong> выбранных вопроса. Каждый ответ на эти вопросы создаст новую запись обратной связи.",
"refreshing_feedback_records": "Обновляем записи отзывов...",
"required": "Обязательно",
"save_changes": "Сохранить изменения",
"select_a_survey_to_see_questions": "Выберите опрос, чтобы увидеть его вопросы",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "Aktuellt datum",
"feedback_record_fields": "Fält för feedbackpost",
"feedback_records": "Feedbackposter",
"feedback_records_refreshed": "Feedbackposter har uppdaterats",
"field_label": "Fältetikett",
"field_type": "Fälttyp",
"formbricks_surveys": "Formbricks Surveys",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> fråga vald. Varje svar på dessa frågor skapar en ny feedbackpost.",
"question_type_not_supported": "Den här frågetypen stöds inte",
"questions_selected": "<strong>{count}</strong> frågor valda. Varje svar på dessa frågor skapar en ny feedbackpost.",
"refreshing_feedback_records": "Uppdaterar feedbackposter...",
"required": "Obligatoriskt",
"save_changes": "Spara ändringar",
"select_a_survey_to_see_questions": "Välj en enkät för att se dess frågor",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "当前日期",
"feedback_record_fields": "反馈记录字段",
"feedback_records": "反馈记录",
"feedback_records_refreshed": "反馈记录已刷新",
"field_label": "字段标签",
"field_type": "字段类型",
"formbricks_surveys": "Formbricks Surveys",
@@ -2106,6 +2107,7 @@
"question_selected": "<strong>{count}</strong> 个问题已选。每个问题的回答都会创建一条新的反馈记录。",
"question_type_not_supported": "不支持此问题类型",
"questions_selected": "<strong>{count}</strong> 个问题已选。每个问题的回答都会创建一条新的反馈记录。",
"refreshing_feedback_records": "正在刷新反馈记录…",
"required": "必填",
"save_changes": "保存更改",
"select_a_survey_to_see_questions": "请选择一个调查以查看其问题",

View File

@@ -2083,6 +2083,7 @@
"feedback_date": "目前日期",
"feedback_record_fields": "回饋紀錄欄位",
"feedback_records": "回饋紀錄",
"feedback_records_refreshed": "回饋紀錄已更新",
"field_label": "欄位標籤",
"field_type": "欄位類型",
"formbricks_surveys": "Formbricks 問卷",
@@ -2106,6 +2107,7 @@
"question_selected": "已選擇 <strong>{count}</strong> 題。每份這些題目的回應都會建立一筆新的意見紀錄。",
"question_type_not_supported": "不支援此題型",
"questions_selected": "已選擇 <strong>{count}</strong> 題。每份這些題目的回應都會建立一筆新的意見紀錄。",
"refreshing_feedback_records": "正在更新回饋紀錄…",
"required": "必填",
"save_changes": "儲存變更",
"select_a_survey_to_see_questions": "請選擇問卷以查看其問題",