From 4385cc99f0bc22054b89f54d89ae65750d4ea294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Garc=C3=ADa=20Isa=C3=ADa?= Date: Mon, 4 Aug 2025 16:47:24 -0300 Subject: [PATCH 1/3] Instantly mark file as being generated on UI See #2385 --- assets/js/reducers/respondentsFiles.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/assets/js/reducers/respondentsFiles.js b/assets/js/reducers/respondentsFiles.js index 96b444020..0e5b01728 100644 --- a/assets/js/reducers/respondentsFiles.js +++ b/assets/js/reducers/respondentsFiles.js @@ -2,6 +2,19 @@ import * as actions from "../actions/survey" export default (state = {}, action) => { switch (action.type) { + case actions.GENERATING_FILE: + return { + ...state, + files: { + ...(state.files || {}), + [action.file]: { + ...(state.files || {})[action.file], + file_type: action.file, + creating: true, + created_at: null + } + } + } case actions.FETCHING_FILES_STATUS: if (action.surveyId != state.surveyId) { return { From 4409d24f46b23aee7e1e9bacf738bb329568095b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Garc=C3=ADa=20Isa=C3=ADa?= Date: Mon, 4 Aug 2025 17:38:55 -0300 Subject: [PATCH 2/3] Animate icon upon file regeneration See #2385 --- assets/css/_card-modal.scss | 16 ++++++++++++++++ .../components/respondents/RespondentIndex.jsx | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/assets/css/_card-modal.scss b/assets/css/_card-modal.scss index 828b8ddaa..99b021346 100644 --- a/assets/css/_card-modal.scss +++ b/assets/css/_card-modal.scss @@ -186,6 +186,22 @@ gap: 0.5rem; } + .spinning { + animation: spin 1s linear infinite; + } + + @keyframes spin { + 0% { + transform: rotate(0deg); + } + 60% { + transform: rotate(360deg); + } + 100% { + transform: rotate(360deg); + } + } + a { display: inline-flex; cursor: pointer; diff --git a/assets/js/components/respondents/RespondentIndex.jsx b/assets/js/components/respondents/RespondentIndex.jsx index d5e63a705..44afc97d8 100644 --- a/assets/js/components/respondents/RespondentIndex.jsx +++ b/assets/js/components/respondents/RespondentIndex.jsx @@ -502,7 +502,7 @@ class RespondentIndex extends Component { const createdAtLabel = fileExists ? : null const fileCreating = !!fileStatus?.creating - const generateButtonClass = fileCreating ? "grey-text" : "black-text" + const generateButtonClass = fileCreating ? "grey-text spinning" : "black-text" const generateButtonOnClick = fileCreating ? null : item.onGenerate const generatingFileLabel = fileCreating ? "Generating..." : "" From cfa4b3ab5eaf97e3c75107769ba1ff3a76812f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Garc=C3=ADa=20Isa=C3=ADa?= Date: Thu, 14 Aug 2025 18:58:15 -0300 Subject: [PATCH 3/3] Extract reducer logic to a function It was a bit convoluted, so naming it helps a bit with readability. --- assets/js/reducers/respondentsFiles.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/assets/js/reducers/respondentsFiles.js b/assets/js/reducers/respondentsFiles.js index 0e5b01728..7949b879d 100644 --- a/assets/js/reducers/respondentsFiles.js +++ b/assets/js/reducers/respondentsFiles.js @@ -1,19 +1,21 @@ import * as actions from "../actions/survey" +const markAsCreating = (files, creatingFile) => ({ + ...files, + [creatingFile]: { + ...files[creatingFile], + file_type: creatingFile, + creating: true, + created_at: null, + } +}) + export default (state = {}, action) => { switch (action.type) { case actions.GENERATING_FILE: return { ...state, - files: { - ...(state.files || {}), - [action.file]: { - ...(state.files || {})[action.file], - file_type: action.file, - creating: true, - created_at: null - } - } + files: markAsCreating(state.files || {}, action.file), } case actions.FETCHING_FILES_STATUS: if (action.surveyId != state.surveyId) {