Skip to content
Commits on Source (37)
......@@ -39,7 +39,7 @@ stages:
- !reference [.rules-map, on-dev]
- !reference [.rules-map, not-on-semantic-release-commit]
- !reference [.rules-map, on-branch]
image: hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.12
image: hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.13
cache:
key:
files:
......
......@@ -6,7 +6,7 @@
meteor-base@1.5.1 # Packages every Meteor app needs to have
mobile-experience@1.1.0 # Packages for a great mobile UX
mongo@1.16.6 # The database Meteor supports right now
mongo@1.16.7 # The database Meteor supports right now
reactive-var@1.0.12 # Reactive variable for tracker
tracker@1.3.2 # Meteor's client-side reactive programming library
......
......@@ -20,7 +20,7 @@ ddp@1.4.1
ddp-client@2.6.1
ddp-common@1.4.0
ddp-rate-limiter@1.2.0
ddp-server@2.6.1
ddp-server@2.6.2
diff-sequence@1.1.2
dynamic-import@0.7.3
ecmascript@0.16.7
......@@ -47,7 +47,7 @@ launch-screen@1.3.0
localstorage@1.2.0
logging@1.3.2
mdg:validated-method@1.3.0
meteor@1.11.2
meteor@1.11.3
meteor-base@1.5.1
meteortesting:browser-tests@1.5.1
meteortesting:mocha@2.1.0
......@@ -61,7 +61,7 @@ modern-browsers@0.1.9
modules@0.19.0
modules-runtime@0.13.1
modules-runtime-hot@0.14.2
mongo@1.16.6
mongo@1.16.7
mongo-decimal@0.1.3
mongo-dev-server@1.1.0
mongo-id@1.0.8
......
# The tag here should match the Meteor version of your app, per .meteor/release
FROM hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.12
FROM hub.eole.education/proxyhub/geoffreybooth/meteor-base:2.13
# Copy app package.json and package-lock.json into container
#COPY ./app/package*.json $APP_SOURCE_FOLDER/
......@@ -14,7 +14,7 @@ RUN bash $SCRIPTS_FOLDER/build-meteor-bundle.sh
# Rather than Node 8 latest (Alpine), you can also use the specific version of Node expected by your Meteor release, per https://docs.meteor.com/changelog.html
FROM hub.eole.education/proxyhub/library/node:14.21.3-alpine
FROM hub.eole.education/proxyhub/meteor/node:14.21.4-alpine3.17
ENV APP_BUNDLE_FOLDER /opt/bundle
ENV SCRIPTS_FOLDER /docker
......@@ -37,7 +37,7 @@ RUN bash $SCRIPTS_FOLDER/build-meteor-npm-dependencies.sh --build-from-source
# Start another Docker stage, so that the final image doesn’t contain the layer with the build dependencies
# See previous FROM line; this must match
FROM hub.eole.education/proxyhub/library/node:14.21.3-alpine
FROM hub.eole.education/proxyhub/meteor/node:14.21.4-alpine3.17
ENV APP_BUNDLE_FOLDER /opt/bundle
ENV SCRIPTS_FOLDER /docker
......
......@@ -7,7 +7,11 @@
"services": {
"sondagesUrl": "http://localhost:3010"
},
"laboiteHost": "http://localhost:3000"
"laboiteHost": "http://localhost:3000",
"matomo": {
"siteIdSondage": 1,
"urlBase": ""
}
},
"keycloak": {
"pubkey": "",
......
......@@ -28,14 +28,19 @@ export function sendEmail(poll, answer) {
sender: Meteor.users.findOne(poll.userId),
date: moment(answer.meetingSlot).format('LLL (Z)'),
});
Email.send({
to: answer.email,
from: Meteor.settings.smtp.fromEmail,
subject: `Sondage - Votre rdv du ${moment(answer.meetingSlot).format('L')}`,
icalEvent: cal.toString(),
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
try {
Email.send({
to: answer.email,
from: Meteor.settings.smtp.fromEmail,
subject: `Sondage - Votre rdv du ${moment(answer.meetingSlot).format('L')}`,
icalEvent: cal.toString(),
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
} catch (error) {
console.log(error);
throw new Meteor.Error('api.events.methods.sendEmail', 'api.errors.cannotSendEmail');
}
}
export function sendEmailToCreator(poll, answer, userId) {
......@@ -56,39 +61,54 @@ export function sendEmailToCreator(poll, answer, userId) {
url: `${Meteor.settings.public.services.sondagesUrl}/poll/answer/${poll._id} `,
connected,
});
Email.send({
to: admin.emails[0].address,
from: Meteor.settings.smtp.fromEmail,
subject: `Rendez-vous - nouveau créneau sélectionné pour le rendez-vous ${poll.title}`,
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
try {
Email.send({
to: admin.emails[0].address,
from: Meteor.settings.smtp.fromEmail,
subject: `Rendez-vous - nouveau créneau sélectionné pour le rendez-vous ${poll.title}`,
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
} catch (error) {
console.log(error);
throw new Meteor.Error('api.events.methods.sendEmailToCreator', 'api.errors.cannotSendEmailToCreator');
}
}
export function sendCancelEmail(poll, answer, content) {
const template = meetingCancelTemplate;
const html = template({ date: moment(answer.meetingSlot).format('LLL (Z)'), content });
Email.send({
to: answer.email,
from: Meteor.settings.smtp.fromEmail,
subject: `Sondage - annulation de votre rendez-vous pour ${poll.title}`,
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
try {
Email.send({
to: answer.email,
from: Meteor.settings.smtp.fromEmail,
subject: `Sondage - annulation de votre rendez-vous pour ${poll.title}`,
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
} catch (error) {
console.log(error);
throw new Meteor.Error('api.events.methods.sendCancelEmail', 'api.errors.cannotSendEmail');
}
}
export function sendEditEmail(poll, email, name, meetingSlot) {
const template = meetingEditTemplate;
const html = template({ date: moment(meetingSlot).format('LLL (Z)'), email, name });
Email.send({
to: email,
from: Meteor.settings.smtp.fromEmail,
subject: `Sondage - Edition de votre rendez-vous pour ${poll.title}`,
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
try {
Email.send({
to: email,
from: Meteor.settings.smtp.fromEmail,
subject: `Sondage - Edition de votre rendez-vous pour ${poll.title}`,
inReplyTo: Meteor.settings.smtp.toEmail,
html,
});
} catch (error) {
console.log(error);
throw new Meteor.Error('api.events.methods.sendEditEmail', 'api.errors.cannotSendEmail');
}
}
export function createEventAgendaMeeting(poll, answer, userId) {
......
......@@ -46,8 +46,10 @@ export const getSinglePollToAnswer = new ValidatedMethod({
},
{ fields: { _id: 1 } },
);
const author = Meteor.users.findOne(poll.userId) || { firstName: '', lastName: '' };
const data = {
poll,
author: { firstName: author.firstName, lastName: author.lastName },
selectedGroups: Groups.find({ _id: { $in: poll.groups } }).fetch(),
answer: this.userId ? PollsAnswers.findOne({ pollId, userId: this.userId }) : null,
};
......
......@@ -74,7 +74,7 @@ export const createPollAnswers = new ValidatedMethod({
throw new Meteor.Error('api.polls_answers.methods.create.notAllowed', 'api.errors.notAllowed');
}
} else if ((poll.public || this.userId) && poll.active) {
sendEmailToCreator(poll, data, this.userId);
if (poll.type === POLLS_TYPES.MEETING) sendEmailToCreator(poll, data, this.userId);
return PollsAnswers.update(
{ pollId: data.pollId, email: data.email },
{ $set: { ...data, userId: this.userId, confirmed: false } },
......
<script>
import { Meteor } from 'meteor/meteor';
import { _ } from 'svelte-i18n';
import { SvelteToast } from '@zerodevx/svelte-toast';
import { onMount } from 'svelte';
import Matomo, { matomo } from '@dexlib/svelte-matomo';
import '/imports/utils/locales/index';
import Router from '/imports/ui/Router.svelte';
......@@ -11,9 +14,16 @@
import PackageJSON from '../../package.json';
let version = PackageJSON.version;
const { matomo: matomoSettings } = Meteor.settings.public;
onMount(() => {
if (matomoSettings?.urlBase) {
matomo.trackPageView();
}
});
let width;
const { setState } = globalState();
const checkDeviceSize = () => {
setState({ mobile: width < 768 });
};
......@@ -29,12 +39,18 @@
<Nav />
<main>
{#if matomoSettings?.urlBase}
<Matomo url={matomoSettings.urlBase} siteId={matomoSettings.siteIdSondage} />
{/if}
<Router />
</main>
<Footer />
<style>
:root {
--toastWidth: 24rem;
}
main {
padding-top: calc(52px + var(--space-between));
min-height: calc(100vh - 64px);
......
......@@ -5,8 +5,8 @@
<section class="hero is-medium is-grey is-bold is-centered">
<div class="hero-body">
<div class="container has-text-centered">
<div class="title is-4">{$_('pages.end.title')}</div>
<div class="content is-4">{$_('pages.end.content')}</div>
<div class="title is-4">{$_('pages.end.title')}<i class="fas fa-check-circle ml-4" color="green" /></div>
<span class="content is-4 is-italic">{$_('pages.end.content')}</span>
</div>
</div>
</section>
......
......@@ -76,7 +76,7 @@
});
$: options = {
initialView: 'listWeek',
initialView: 'timeGridWeek',
plugins: [timeGridPlugin, interactionPlugin, listView],
firstDay: 1,
allDaySlot: false,
......@@ -94,7 +94,7 @@
timeGridWeek: $_('pages.answer.timeGridWeek'),
},
headerToolbar: {
left: 'listWeek timeGridWeek',
left: 'timeGridWeek listWeek',
},
locale: $locale,
timeZone: 'local',
......
......@@ -24,6 +24,7 @@
console.log(e);
toast.push($_(e.reason), toasts.error);
} else {
toast.push($_(`components.MeetingAnswerCancel.${emailNotice ? 'userNotified' : 'userNotNotified'}`));
router.goto(ROUTES.ANSWER_POLL_RM(answer.pollId));
}
});
......
......@@ -35,6 +35,7 @@
console.log(e);
toast.push($_(e.reason), toasts.error);
} else {
toast.push($_(`components.MeetingAnswerEdit.${emailNotice ? 'userNotified' : 'userNotNotified'}`));
router.goto(ROUTES.ANSWER_POLL_RM(answer.pollId));
}
});
......
......@@ -28,6 +28,7 @@
export let meta;
let selectedGroups;
let poll = {};
let author = { firstName: '', lastName: '' };
let loading = false;
let askToConnect = false;
let answer = {
......@@ -47,6 +48,7 @@
loading = false;
if (r) {
poll = r.poll;
author = r.author;
selectedGroups = r.selectedGroups;
if (r.answer) {
answer = r.answer;
......@@ -164,12 +166,17 @@
{#if poll._id}
<div class="box">
<div class="columns is-multiline">
<div class="column is-full">
<div class="column is-half">
<div class="field">
<label class="label">{$_('pages.answer.description')}</label>
{poll.description || ''}
</div>
</div>
<div class="column is-half">
<label class="label">{$_('pages.answer.author')}</label>
{author.firstName}
{author.lastName} ({moment(poll.updatedAt).format('LLL')})
</div>
<div class="column is-half">
<div class="field">
<div class="control">
......
......@@ -69,6 +69,12 @@
return '00:10:00';
case '00:15':
return '00:15:00';
case '00:20':
return '00:20:00';
case '00:25':
return '00:25:00';
case '00:45':
return '00:45:00';
default:
return '00:30:00';
}
......
......@@ -200,7 +200,8 @@
</div>
<div class="column is-half-desktop is-full-mobile is-right">
<BigLink
disabled={!$newPollStore.title}
disabled={(!$newPollStore.title || !$newPollStore.public) &&
(!$newPollStore.title || !!$newPollStore.groups.length == 0)}
link={meta.params._id ? ROUTES.EDIT_POLL_RM(meta.params._id, 2) : ROUTES.NEW_POLL_RM(2)}
text={$_('pages.new_poll.next')}
/>
......
......@@ -60,7 +60,6 @@
$newPollStore.dates[i].slots[index] = time;
});
};
</script>
<svelte:head>
......@@ -122,7 +121,7 @@
{/if}
<tbody>
{#each $newPollStore.dates as day}
{#each $newPollStore.dates.sort((a, b) => a.date - b.date) as day}
<tr>
<th>
{moment(day.date).format($_('components.Time.dateFormat'))}
......@@ -195,5 +194,4 @@
.fa-trash {
color: red;
}
</style>
......@@ -59,7 +59,17 @@
{
data: {
...$newPollStore,
dates: $newPollStore.type === POLLS_TYPES.POLL ? $newPollStore.dates.sort((a, b) => a.date - b.date) : [],
dates:
$newPollStore.type === POLLS_TYPES.POLL
? $newPollStore.dates
.sort((a, b) => a.date - b.date)
.map((date) => {
return {
date: date.date,
slots: date.slots.sort(),
};
})
: [],
meetingSlots:
$newPollStore.type === POLLS_TYPES.MEETING
? $newPollStore.meetingSlots.sort((a, b) => a.start - b.start)
......@@ -187,7 +197,7 @@
</div>
{/each}
{:else}
{#each $newPollStore.dates as day}
{#each $newPollStore.dates.sort((a, b) => a.date - b.date) as day}
<div class="column is-one-quarter">
<div class="block">
<div class="title is-6">
......@@ -197,7 +207,7 @@
{#if $newPollStore.allDay}
{$_('pages.new_poll_3.allDay_input')}
{:else}
{#each day.slots as slot}
{#each day.slots.sort() as slot}
<InputTimePicker date={day.date} duration={$newPollStore.duration} value={slot} disabled={true} />
{/each}
{/if}
......
......@@ -17,31 +17,33 @@
<h3 class="title is-5">{$_('pages.new_poll_2.selected_dates')}</h3>
<Divider />
{#if dates.length}
<table class="table is-striped is-fullwidth" style="margin-top: -3vh;">
<tbody>
{#each dates as date}
<tr>
<th>
{#if meetingSlots}
{moment(date.start).format($_('components.Time.dateFormat'))} /
{moment(date.start).format('HH:mm')}
-
{moment(date.end).format('HH:mm')}
{:else}
{moment(date.date).format($_('components.Time.dateFormat'))}
{/if}
</th>
<th>
<button on:click={() => removeDate(date)} class="button is-rounded is-primary is-small mt-2">
<span class="icon is-small">
<i class="fas fa-trash" />
</span>
</button>
</th>
</tr>
{/each}
</tbody>
</table>
<div style="height:{meetingSlots ? '84vh' : '37vh'}; overflow-y: auto">
<table class="table is-striped is-fullwidth">
<tbody>
{#each dates.sort((a, b) => a.date - b.date) as date}
<tr>
<th>
{#if meetingSlots}
{moment(date.start).format($_('components.Time.dateFormat'))} /
{moment(date.start).format('HH:mm')}
-
{moment(date.end).format('HH:mm')}
{:else}
{moment(date.date).format($_('components.Time.dateFormat'))}
{/if}
</th>
<th>
<button on:click={() => removeDate(date)} class="button is-rounded is-primary is-small mt-2">
<span class="icon is-small">
<i class="fas fa-trash" />
</span>
</button>
</th>
</tr>
{/each}
</tbody>
</table>
</div>
{:else}
<NoResults title={$_('pages.new_poll_2.no_selected_dates')} />
{/if}
......