Skip to content

Commit

Permalink
feat: Image Docker custom de grist aux couleur de la Marianne
Browse files Browse the repository at this point in the history
  • Loading branch information
hexaltation committed Jun 10, 2024
1 parent f03d125 commit 2512506
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/docker-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ jobs:
image: lasuite/keycloak-apps
path: dockerfiles/keycloak-apps
cmd_version: "echo \"$(cat dockerfiles/keycloak-apps/Dockerfile | head -n1 | cut -d' ' -f2)\" >> $GITHUB_ENV"
- dockerfile: dockerfiles/grist/Dockerfile
image: lasuite/grist
path: dockerfiles/grist
cmd_version: "grep -Po \"GRIST_\\KVERSION=.*\" dockerfiles/grist/Dockerfile | head -n 1 >> $GITHUB_ENV"
steps:
-
uses: actions/create-github-app-token@v1
Expand Down
41 changes: 41 additions & 0 deletions dockerfiles/grist/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
ARG GRIST_VERSION=1.1.14

FROM gristlabs/grist:$GRIST_VERSION

WORKDIR /grist/static

ARG LASUITE_VERSION=1.0.1
ARG LASUITE_ARCHIVE=gouvfr-lasuite-integration-$LASUITE_VERSION.tgz

RUN apt-get update
RUN apt-get install -y wget

RUN wget https://github.com/numerique-gouv/lasuite-integration/releases/download/integration-v$LASUITE_VERSION/$LASUITE_ARCHIVE
RUN tar -zxvf $LASUITE_ARCHIVE

# Archive is extracted as "package"
# We move it to @gouvfr-lasuite/integration to be complient with
# https://integration.lasuite.numerique.gouv.fr/guides/gaufre/
RUN mkdir @gouvfr-lasuite
RUN mv package @gouvfr-lasuite/integration

RUN rm $LASUITE_ARCHIVE

COPY ressources/* ./
RUN mv marianne-48x48.png ui-icons/Logo/

RUN apt-get remove --purge -y wget

WORKDIR /grist

RUN \
groupadd grist -g 10022 && \
useradd -ms /bin/bash grist -g grist -u 10022 && \
chown -R grist:grist /grist && \
chown -R grist:grist /persist

USER grist

# Variable to force grist to use custom.css and dinum-custom.js
ENV APP_STATIC_INCLUDE_CUSTOM_CSS true
ENV GRIST_INCLUDE_CUSTOM_SCRIPT_URL /v/unknown/dinum-custom.js
59 changes: 59 additions & 0 deletions dockerfiles/grist/ressources/custom.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
@import url("@gouvfr-lasuite/integration/dist/css/gaufre.css");

:root {
/* logo */
--icon-GristLogo: url("ui-icons/Logo/marianne-48x48.png") !important;
--grist-logo-bg: #ffffff !important;
--grist-logo-size: 22px 22px !important;

/* colors */
--grist-color-light-grey: #f6f6f6 !important;
--grist-color-medium-grey: #dddddd99 !important;
--grist-color-medium-grey-opaque: #e5e5e5 !important;
--grist-color-dark-grey: #dddddd !important;
--grist-color-light: #ffffff !important;
--grist-color-dark: #2f2f2f !important;
--grist-color-dark-bg: #2f2f2f !important;
--grist-color-slate: #929292 !important;
--grist-color-light-green: #000091 !important;
--grist-color-dark-green: #2323ff !important;
--grist-color-darker-green: #2323ff !important;
--grist-color-lighter-green: #cacafb !important;
--grist-color-lighter-blue: #0078f3 !important;
--grist-color-light-blue: #0063cb !important;
--grist-color-cursor: #000091 !important;
--grist-color-selection: #ececfe !important;
--grist-color-selection-opaque: #ececfe !important;
--grist-color-selection-darker-opaque: #e3e3fd !important;
--grist-color-inactive-cursor: #cacafb !important;
--grist-color-hover: #cecece !important;
--grist-color-error: #d64d00 !important;
--grist-color-warning: #fea941 !important;
--grist-color-warning-bg: #dd962c !important;
--grist-color-backdrop: #3a3a3a !important;
--grist-label-text-bg: #ffffff !important;
--grist-label-active-bg: #f0f0f0 !important;
--grist-primary-fg: #000091 !important;
--grist-primary-fg-hover: #2323ff !important;
--grist-primary-bg: #ffffff !important;
--grist-control-bg: #ffffff !important;
--grist-control-fg: #000091 !important;
--grist-primary-fg-hover: #2323ff !important;
--grist-control-border: 1px solid #2323ff !important;
--grist-toast-bg: #040404 !important;

/* Custom inputs colors*/
--grist-actual-cell-color: #6e6ef2 !important;
--accent-color: #6e6ef2 !important;
}

.test-rule-permissions [class*=deny] {
background-color: #ff0000;
background-image: linear-gradient(-45deg, #ff0000 14px, white 15px 16px, #ff0000 16px);
border-color: #ff0000;
}

.test-rule-permissions [class*=allow] {
background-color: #16b378;
border-color: #16b378;
}
147 changes: 147 additions & 0 deletions dockerfiles/grist/ressources/dinum-custom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
// ========================
// START dialog
// ========================
(function maintainanceDialog() {
// CONFIGURER ICI
const config = {
// CEST = heure d'été (+0200), CET = heure d'hiver ().
selectedTz: 'CEST',

// Date du début de la maintenance
startDateWithoutTimezone: '2024-04-18T17:30:00',

// Durée de la maitenance prévue (en minutes).
// ASTUCE: Si la maintenance dure moins longtemps que prévu, réduire a postériori la valeur de cette variable de sorte à ce qu'elle n'apparaisse plus.
// Pas besoin de committer ensuite le changement dans le repo.
maintainanceDurationMinutes: 60,
};
// FIN CONFIGURER

const tz = { CEST: '0200', CET: '0100' };

const maintainanceStartDate = new Date(`${config.startDateWithoutTimezone}+${tz[config.selectedTz]}`);
const maintainanceEndDate = (function () {
const date = new Date(maintainanceStartDate); // If I may, Date API is really ackward, especially because it is not immutable
date.setMinutes(maintainanceStartDate.getMinutes() + config.maintainanceDurationMinutes);
return date;
})();


function showDialog() {
const dialog = document.createElement('dialog');
dialog.id = 'maintenancePopin';
const dateFormatter = new Intl.DateTimeFormat('fr-FR', {
dateStyle: 'full',
timeStyle: 'short',
timeZone: 'Europe/Paris',
});
const timeFormatter = new Intl.DateTimeFormat('fr-FR', {
timeStyle: 'short',
timeZone: 'Europe/Paris',
});

dialog.innerHTML = `
<p>
Une maintenance de Grist est prévue le ${dateFormatter.format(maintainanceStartDate)} heure de Paris
jusqu'à ${timeFormatter.format(maintainanceEndDate)}, période durant laquelle le service sera momentanément indisponible. <br>
Pour toute remarque ou question, merci de nous contacter via <a href="mailto:[email protected]">[email protected]</a></p>
<p>Merci de votre compréhension</p>
<form method="dialog">
<p>
<input type="checkbox" id="jaiCompris">
<label for="jaiCompris">J'ai lu et compris, merci de ne plus me montrer ce message</label>
</p>
<button disabled id="fermerPopinMaintenance">Fermer</button>
</form>
`;
document.body.appendChild(dialog);
document.getElementById('fermerPopinMaintenance').onclick = function onMessageUnderstood() {
localStorage.maintainanceStartDateAgreement = maintainanceStartDate.toISOString();
}
document.getElementById('jaiCompris').onchange = function (ev) {
document.getElementById('fermerPopinMaintenance').disabled = !ev.target.checked;
}
dialog.showModal();
}

if (Date.now() < maintainanceEndDate.getTime() &&
(!localStorage.maintainanceStartDateAgreement || localStorage.maintainanceStartDateAgreement !== maintainanceStartDate.toISOString())) {
window.addEventListener('load', showDialog);
}
})();

// ========================
// END dialog
// ========================

// ========================
// START gauffre
// ========================


window.addEventListener('load', async (event) => {
await waitForElm('body.interface-full');
// gristApp.topAppModel.appObs is not populated at that point, listen for the observer to their first change

let listener;
listener = gristApp.topAppModel.appObs.addListener( async (appObs) => {
// Gauffre must be displayed only in home pages
if (appObs.pageType.get() !== "home"){
return 1;
}
// We wait for header bar to be available in DOM to insert Gauffre in it
await waitForElm('.test-top-header');

// Create gauffre button Tag
const gristBar = document.getElementsByClassName('test-top-header')[0];
const gauffreDiv = document.createElement('div');
const gauffreButton = document.createElement('button');
const gauffreText = "Les services de La Suite numérique";

gauffreDiv.className = 'gauffre-container';
gauffreButton.type = "button";
gauffreButton.className = "lasuite-gaufre-btn lasuite-gaufre-btn--vanilla js-lasuite-gaufre-btn";
gauffreButton.title = gauffreText;
gauffreButton.text = gauffreText;
gauffreDiv.appendChild(gauffreButton);
gristBar.insertBefore(gauffreDiv, gristBar.lastChild);

// Create gauffre script Tag
const gauffreScript = document.createElement('script');

gauffreScript.id = "lasuite-gaufre-script";
gauffreScript.setAttribute('async', true);
gauffreScript.setAttribute('defer', true);
gauffreScript.src = "https://integration.lasuite.numerique.gouv.fr/api/v1/gaufre.js";
document.head.insertBefore(gauffreScript, document.head.lastChild);

// Should be disposed so we only listen for changes once, but failed to achieve doing that.
// listener.dispose();
});
});

// from https://stackoverflow.com/a/61511955
function waitForElm(selector) {
return new Promise(resolve => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}

const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});

// If you get "parameter 1 is not of type 'Node'" error, see https://stackoverflow.com/a/77855838/492336
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}

// ========================
// END gauffre
// ========================
Binary file added dockerfiles/grist/ressources/marianne-48x48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 2512506

Please sign in to comment.