@saooti/octopus-sdk 31.0.21 → 31.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +3 -0
  2. package/package.json +2 -2
  3. package/src/assets/live.scss +13 -13
  4. package/src/assets/multiselect.scss +1 -1
  5. package/src/components/display/categories/CategoryChooser.vue +1 -0
  6. package/src/components/display/comments/CommentBasicView.vue +1 -0
  7. package/src/components/display/emission/EmissionChooser.vue +1 -0
  8. package/src/components/display/emission/EmissionItem.vue +1 -0
  9. package/src/components/display/emission/EmissionPlayerItem.vue +1 -0
  10. package/src/components/display/filter/ProductorSearch.vue +2 -0
  11. package/src/components/display/organisation/OrganisationChooser.vue +1 -0
  12. package/src/components/display/participant/ParticipantItem.vue +1 -0
  13. package/src/components/display/playlist/PlaylistItem.vue +1 -0
  14. package/src/components/display/podcasts/ParticipantDescription.vue +1 -0
  15. package/src/components/display/podcasts/PodcastImage.vue +1 -0
  16. package/src/components/display/podcasts/TagList.vue +2 -0
  17. package/src/components/display/rubriques/RubriqueChooser.vue +1 -0
  18. package/src/components/display/sharing/QrCode.vue +24 -4
  19. package/src/components/display/sharing/ShareButtons.vue +1 -0
  20. package/src/components/display/sharing/SharePlayer.vue +1 -1
  21. package/src/components/form/ClassicCheckbox.vue +6 -1
  22. package/src/components/form/ClassicLoading.vue +2 -2
  23. package/src/components/form/ClassicRadio.vue +6 -1
  24. package/src/components/misc/ErrorMessage.vue +1 -0
  25. package/src/components/misc/player/Player.vue +2 -1
  26. package/src/components/mixins/player/playerLive.ts +9 -5
  27. package/src/components/mixins/player/playerLogic.ts +22 -28
  28. package/src/components/pages/Participant.vue +1 -0
  29. package/src/locale/de.ts +1 -0
  30. package/src/locale/en.ts +1 -0
  31. package/src/locale/es.ts +1 -0
  32. package/src/locale/fr.ts +1 -0
  33. package/src/locale/it.ts +1 -0
  34. package/src/locale/sl.ts +1 -0
  35. package/src/sass/_variables.scss +2 -0
package/README.md CHANGED
@@ -597,3 +597,6 @@ See [Configuration Reference](https://cli.vuejs.org/config/).
597
597
  * 31.0.19 Add access_token player
598
598
  * 31.0.20 Player mobile
599
599
  * 31.0.21 Player mobile
600
+ * 31.0.22 Merge 30
601
+ * 31.0.23 Qr Code option noir
602
+ * 31.0.24 Amélioration accessibilité
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saooti/octopus-sdk",
3
- "version": "31.0.21",
3
+ "version": "31.0.24",
4
4
  "private": false,
5
5
  "description": "Javascript SDK for using octopus",
6
6
  "author": "Saooti",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "main": "./dist/octopus.common.js",
16
16
  "dependencies": {
17
- "@saooti/octopus-api": "^0.31.2",
17
+ "@saooti/octopus-api": "^0.31.3",
18
18
  "@vue/cli": "^5.0.4",
19
19
  "@vue/compat": "^3.2.31",
20
20
  "axios": "^0.24.0",
@@ -1,39 +1,39 @@
1
1
  .planned-bg {
2
- background: #f09653;
2
+ background: rgb(188, 88, 16);
3
3
  }
4
4
  .pending-bg {
5
- background: #f1643e;
5
+ background: rgb(216, 58, 14);
6
6
  }
7
7
  .recording-bg {
8
- background: #f34a4a;
8
+ background: rgb(233, 12, 12);
9
9
  }
10
10
  .error-bg {
11
- background: #ff0000;
11
+ background: rgb(173, 0, 0);
12
12
  }
13
13
  .debriefing-bg {
14
- background: #6ec66e;
14
+ background: rgb(54, 134, 54);
15
15
  }
16
16
  .done-bg {
17
- background: #679fe9;
17
+ background: rgb(42, 117, 218);
18
18
  }
19
19
  .publishing-bg {
20
- background: #7d7d7d;
20
+ background: rgb(112, 112, 112);
21
21
  }
22
22
  .planned-shadow {
23
- box-shadow: 0px 12px 48px 6px rgba(240, 151, 83, 0.2);
23
+ box-shadow: 0px 12px 48px 6px rgba(188, 88, 16, 0.2);
24
24
  }
25
25
  .pending-shadow {
26
- box-shadow: 0px 12px 48px 6px rgba(241, 101, 62, 0.2);
26
+ box-shadow: 0px 12px 48px 6px rgba(216, 58, 14, 0.2);
27
27
  }
28
28
  .recording-shadow {
29
- box-shadow: 0px 12px 48px 6px rgba(243, 74, 74, 0.2);
29
+ box-shadow: 0px 12px 48px 6px rgba(233, 12, 12, 0.2);
30
30
  }
31
31
  .debriefing-shadow {
32
- box-shadow: 0px 12px 48px 6px rgba(110, 198, 110, 0.2);
32
+ box-shadow: 0px 12px 48px 6px rgba(54, 134, 54, 0.2);
33
33
  }
34
34
  .publishing-shadow {
35
- box-shadow: 0px 12px 48px 6px rgba(125, 125, 125, 0.2);
35
+ box-shadow: 0px 12px 48px 6px rgba(112, 112, 112, 0.2);
36
36
  }
37
37
  .error-shadow {
38
- box-shadow: 0px 12px 48px 6px rgba(255, 0, 0, 0.2);
38
+ box-shadow: 0px 12px 48px 6px rgba(173, 0, 0, 0.2);
39
39
  }
@@ -315,7 +315,7 @@
315
315
  }
316
316
 
317
317
  .multiselect__placeholder {
318
- color: #adadad;
318
+ color: #757575;
319
319
  display: inline-block;
320
320
  margin-bottom: 10px;
321
321
  padding-top: 2px;
@@ -15,6 +15,7 @@
15
15
  :disabled="isDisabled"
16
16
  label="name"
17
17
  track-by="id"
18
+ :aria-expanded="false"
18
19
  :placeholder="$t('Type string to filter by categories')"
19
20
  :options="categories"
20
21
  :multiple="multiple"
@@ -7,6 +7,7 @@
7
7
  >{{ $t('Live') }}</b>
8
8
  <b
9
9
  :id="'popover-comment' + comment.comId"
10
+ role="button"
10
11
  tabindex="-1"
11
12
  :class="editRight || isValid? '':'text-danger'"
12
13
  class="me-2"
@@ -14,6 +14,7 @@
14
14
  v-model="emission"
15
15
  label="name"
16
16
  track-by="emissionId"
17
+ :aria-expanded="false"
17
18
  :placeholder="$t('Type string to filter by emission')"
18
19
  :options="emissions"
19
20
  :multiple="false"
@@ -14,6 +14,7 @@
14
14
  <img
15
15
  v-lazy="emission.imageUrl"
16
16
  class="img-box"
17
+ :title="$t('Emission name image', {name:emission.name})"
17
18
  >
18
19
  <div class="emission-item-text">
19
20
  <div
@@ -21,6 +21,7 @@
21
21
  >
22
22
  <img
23
23
  v-lazy="emission.imageUrl"
24
+ :title="$t('Emission name image', {name:emission.name})"
24
25
  class="img-box rounded-0"
25
26
  >
26
27
  <div
@@ -13,6 +13,8 @@
13
13
  v-if="!!organisationId"
14
14
  v-model:textInit="keepOrganisation"
15
15
  class="m-3"
16
+ :label="$t('check this box if you want to keep this filter for the rest of your visit')"
17
+ :display-label="false"
16
18
  id-checkbox="organisation-checkbox"
17
19
  @clickAction="onKeepOrganisation"
18
20
  />
@@ -15,6 +15,7 @@
15
15
  v-model="organisation"
16
16
  label="name"
17
17
  track-by="id"
18
+ :aria-expanded="false"
18
19
  :placeholder="$t('Type string to filter by organisation')"
19
20
  :options="organisations"
20
21
  :multiple="false"
@@ -14,6 +14,7 @@
14
14
  >
15
15
  <img
16
16
  v-lazy="participant.imageUrl"
17
+ :title="$t('Animator image')"
17
18
  class="img-box-circle"
18
19
  >
19
20
  <div class="participant-name">
@@ -14,6 +14,7 @@
14
14
  >
15
15
  <img
16
16
  v-lazy="playlist.imageUrl"
17
+ :title="$t('Playlist name image', {name:name})"
17
18
  class="img-box"
18
19
  >
19
20
  </router-link>
@@ -6,6 +6,7 @@
6
6
  {{ title }}
7
7
  <span
8
8
  :id="idPopover"
9
+ role="button"
9
10
  tabindex="-1"
10
11
  class="saooti-help m-0"
11
12
  :title="title"
@@ -6,6 +6,7 @@
6
6
  <img
7
7
  v-lazy="podcast.imageUrl"
8
8
  class="img-box"
9
+ :alt="$t('Episode name image', {name:podcast.title})"
9
10
  >
10
11
  <template v-if="isPodcastmaker">
11
12
  <div
@@ -13,10 +13,12 @@
13
13
  >
14
14
  <div
15
15
  :id="'tag-list-from-podcast-page'+index"
16
+ role="button"
16
17
  >
17
18
  <img
18
19
  v-if="isOuestFranceTag(tag)"
19
20
  class="ouest-france-logo"
21
+ alt="Logo Ouest France"
20
22
  src="/img/ouest_france_logo.svg"
21
23
  >
22
24
  {{ formateOfTag(tag) }}
@@ -16,6 +16,7 @@
16
16
  class="rubriqueChooser"
17
17
  label="name"
18
18
  track-by="rubriqueId"
19
+ :aria-expanded="false"
19
20
  :placeholder="$t('Type string to filter by rubrics')"
20
21
  :options="rubriques"
21
22
  :multiple="multiple"
@@ -8,6 +8,13 @@
8
8
  class="myQrCode"
9
9
  :margin="2"
10
10
  />
11
+ <ClassicCheckbox
12
+ v-if="'#000000'!==otherColor"
13
+ v-model:textInit="isNotBlack"
14
+ class="flex-shrink-0"
15
+ id-checkbox="is-black-qr-code"
16
+ :label="$t('Use organization color')"
17
+ />
11
18
  <button
12
19
  class="btn m-3"
13
20
  @click="download"
@@ -22,6 +29,7 @@
22
29
  </template>
23
30
 
24
31
  <script lang="ts">
32
+ import ClassicCheckbox from '../../form/ClassicCheckbox.vue';
25
33
  import { state } from '../../../store/paramStore';
26
34
  import octopusApi from '@saooti/octopus-api';
27
35
  import Snackbar from '../../misc/Snackbar.vue';
@@ -33,7 +41,8 @@ export default defineComponent({
33
41
 
34
42
  components: {
35
43
  Snackbar,
36
- QrcodeVue
44
+ QrcodeVue,
45
+ ClassicCheckbox
37
46
  },
38
47
  props: {
39
48
  url: { default: '', type: String},
@@ -42,7 +51,9 @@ export default defineComponent({
42
51
  data() {
43
52
  return {
44
53
  size: 200 as number,
45
- color: "#40a372" as string
54
+ color: "#000000" as string,
55
+ otherColor:"#000000" as string,
56
+ isNotBlack: false as boolean,
46
57
  };
47
58
  },
48
59
  computed:{
@@ -50,6 +61,15 @@ export default defineComponent({
50
61
  return (state.generalParameters.authenticated as boolean);
51
62
  },
52
63
  },
64
+ watch:{
65
+ isNotBlack(){
66
+ if(this.isNotBlack){
67
+ this.color = this.otherColor;
68
+ }else{
69
+ this.color = "#000000";
70
+ }
71
+ }
72
+ },
53
73
  created(){
54
74
  this.initColor();
55
75
  },
@@ -66,7 +86,7 @@ export default defineComponent({
66
86
  },
67
87
  async initColor(): Promise<void> {
68
88
  if(state.generalParameters.podcastmaker && state.generalParameters.podcastmakerColor){
69
- this.color = state.generalParameters.podcastmakerColor;
89
+ this.otherColor = state.generalParameters.podcastmakerColor;
70
90
  return;
71
91
  }
72
92
  if (!this.authenticated) return;
@@ -79,7 +99,7 @@ export default defineComponent({
79
99
  );
80
100
  }
81
101
  if (Object.prototype.hasOwnProperty.call(data,'COLOR')) {
82
- this.color = data.COLOR;
102
+ this.otherColor = data.COLOR;
83
103
  }
84
104
  },
85
105
  }
@@ -13,6 +13,7 @@
13
13
  <span
14
14
  v-if="authenticated"
15
15
  id="popover-share-help"
16
+ role="button"
16
17
  tabindex="-1"
17
18
  class="saooti-help ms-2"
18
19
  :title="$t('Help')"
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div>
3
- <div class="module-box text-center-mobile">
3
+ <div class="module-box text-center-mobile overflow-visible">
4
4
  <h3>{{ $t('Embed') }}</h3>
5
5
  <template v-if="!exclusive && (authenticated || notExclusive)">
6
6
  <div class="d-flex flex-column align-items-center">
@@ -9,10 +9,13 @@
9
9
  type="checkbox"
10
10
  class="form-check-input"
11
11
  :disabled="isDisabled"
12
+ :title="displayLabel?'':label"
13
+ :data-selenium="selenium"
12
14
  @click="emitClickAction"
13
15
  >
14
16
  <label
15
17
  class="form-check-label"
18
+ :class="displayLabel? '': 'd-none'"
16
19
  :for="idCheckbox"
17
20
  >{{ label }}</label>
18
21
  </div>
@@ -28,7 +31,9 @@ export default defineComponent({
28
31
  label: { default: '', type: String },
29
32
  isDisabled: { default: false, type: Boolean },
30
33
  textInit: { default: false, type: Boolean },
31
- isSwitch:{default:false, type:Boolean}
34
+ isSwitch:{default:false, type:Boolean},
35
+ displayLabel: {default:true, type: Boolean},
36
+ selenium: { default: '', type: String },
32
37
  },
33
38
  emits: ['update:textInit', 'clickAction'],
34
39
 
@@ -4,9 +4,9 @@
4
4
  class="d-flex justify-content-center"
5
5
  >
6
6
  <div class="spinner-border me-3" />
7
- <h3 class="mt-2">
7
+ <div class="h3 mt-2">
8
8
  {{ loadingText }}
9
- </h3>
9
+ </div>
10
10
  </div>
11
11
  <div
12
12
  v-else-if="errorText"
@@ -1,9 +1,13 @@
1
1
  <template>
2
- <div class="d-flex flex-column">
2
+ <div
3
+ class="d-flex"
4
+ :class="isColumn? 'flex-column':''"
5
+ >
3
6
  <div
4
7
  v-for="option in options"
5
8
  :key="option.title"
6
9
  class="form-check"
10
+ :class="isColumn? '':'me-2'"
7
11
  >
8
12
  <input
9
13
  :id="idRadio + option.value"
@@ -34,6 +38,7 @@ export default defineComponent({
34
38
  isDisabled: { default: false, type: Boolean },
35
39
  options: { default: ()=>[], type: Array as () => Array<{title: string, value: string|undefined}> },
36
40
  textInit: { default: undefined, type: String },
41
+ isColumn: {default:true, type:Boolean}
37
42
  },
38
43
  emits: ['update:textInit'],
39
44
 
@@ -8,6 +8,7 @@
8
8
  >
9
9
  <img
10
10
  src="/img/caution.png"
11
+ alt="Caution"
11
12
  class="icon-caution"
12
13
  >
13
14
  <div class="alert-text">
@@ -11,7 +11,7 @@
11
11
  <template v-if="display">
12
12
  <audio
13
13
  id="audio-player"
14
- :src="!live? audioUrl: undefined"
14
+ :src="!live? audioUrlToPlay: undefined"
15
15
  autoplay
16
16
  @timeupdate="onTimeUpdate"
17
17
  @ended="onFinished"
@@ -82,6 +82,7 @@ export default defineComponent({
82
82
  comments: [] as Array<CommentPodcast>,
83
83
  showTimeline: false as boolean,
84
84
  largeVersion: false as boolean,
85
+ audioUrlToPlay: "" as string
85
86
  };
86
87
  },
87
88
  computed: {
@@ -100,11 +100,15 @@ export const playerLive = defineComponent({
100
100
  },
101
101
  async endListeningProgress(): Promise<void> {
102
102
  if (!this.downloadId) return;
103
- await octopusApi.updatePlayerTime(
104
- this.downloadId,
105
- Math.round(this.listenTime)
106
- );
107
- this.setDownloadId(null);
103
+ try {
104
+ await octopusApi.updatePlayerTime(
105
+ this.downloadId,
106
+ Math.round(this.listenTime)
107
+ );
108
+ } catch{
109
+ //Do nothing
110
+ }
111
+ this.downloadId = null;
108
112
  this.notListenTime = 0;
109
113
  this.lastSend = 0;
110
114
  this.listenTime = 0;
@@ -23,6 +23,7 @@ export const playerLogic = defineComponent({
23
23
  hlsReady: false as boolean,
24
24
  comments: [] as Array<CommentPodcast>,
25
25
  showTimeline: false as boolean,
26
+ audioUrlToPlay: "" as string
26
27
  };
27
28
  },
28
29
  computed: {
@@ -44,8 +45,15 @@ export const playerLogic = defineComponent({
44
45
  },
45
46
 
46
47
  watch: {
47
- audioUrl(): void{
48
+ async audioUrl(): Promise<void>{
48
49
  this.playerError = false;
50
+ if(this.media || !this.podcast || !this.podcast.availability.visibility ||this.listenError){
51
+ this.audioUrlToPlay = this.audioUrl;
52
+ }
53
+ if(!this.podcast){return;}
54
+ const response = await octopusApi.fetchPodcastDownloadUrl("podcast/download/register/"+this.podcast.podcastId+".mp3"+ this.audioUrl);
55
+ this.setDownloadId(response.downloadId.toString());
56
+ this.audioUrlToPlay = response.location;
49
57
  },
50
58
  podcast: {
51
59
  deep: true,
@@ -88,6 +96,14 @@ export const playerLogic = defineComponent({
88
96
  },
89
97
 
90
98
  methods: {
99
+ getDomain(): string{
100
+ let domain = "";
101
+ const domainArray: RegExpExecArray | null = /\.(.+)/.exec(window.location.host);
102
+ if(domainArray && null !== domainArray){
103
+ domain = domainArray[1];
104
+ }
105
+ return domain;
106
+ },
91
107
  getAudioUrl(): string{
92
108
  if (this.media) return this.media.audioUrl? this.media.audioUrl:"";
93
109
  if (!this.podcast) return '';
@@ -96,7 +112,6 @@ export const playerLogic = defineComponent({
96
112
  if (this.listenError) return this.podcast.audioStorageUrl;
97
113
  const parameters = [];
98
114
  parameters.push('origin=octopus');
99
- parameters.push('cookieName=player_' + this.podcast.podcastId);
100
115
  parameters.push('listenerId='+this.getListenerId());
101
116
  if (
102
117
  this.$store.state.authentication &&
@@ -109,7 +124,7 @@ export const playerLogic = defineComponent({
109
124
  if("SECURED" === this.podcast.organisation.privacy && this.$store.state.authentication.isAuthenticated && this.$store.state.oAuthParam.accessToken){
110
125
  parameters.push('access_token='+this.$store.state.oAuthParam.accessToken);
111
126
  }
112
- return this.podcast.audioUrl + '?' + parameters.join('&');
127
+ return this.podcast.podcastId + '.mp3?' + parameters.join('&');
113
128
  },
114
129
  reInitPlayer():void{
115
130
  this.setDownloadId(null);
@@ -123,12 +138,7 @@ export const playerLogic = defineComponent({
123
138
  let listenerId = this.getCookie("octopus_listenerId");
124
139
  if(!listenerId){
125
140
  listenerId = new Date().valueOf().toString() + Math.random();
126
- let domain = "";
127
- const domainArray: RegExpExecArray | null = /\.(.+)/.exec(window.location.host);
128
- if(domainArray && null !== domainArray){
129
- domain = domainArray[1];
130
- }
131
- this.setCookie("octopus_listenerId", listenerId, ';domain='+domain);
141
+ this.setCookie("octopus_listenerId", listenerId, ';domain='+this.getDomain());
132
142
  }
133
143
  return listenerId;
134
144
  },
@@ -153,9 +163,9 @@ export const playerLogic = defineComponent({
153
163
  );
154
164
  },
155
165
  onError(): void {
156
- if (this.podcast && !this.listenError) {
166
+ if (this.podcast && ""!==this.audioUrlToPlay && !this.listenError) {
157
167
  this.listenError = true;
158
- } else if (this.podcast || this.media) {
168
+ } else if ((this.podcast && ""!==this.audioUrlToPlay ) || this.media) {
159
169
  this.playerError = true;
160
170
  }
161
171
  },
@@ -163,7 +173,7 @@ export const playerLogic = defineComponent({
163
173
  const mediaTarget = (event.currentTarget as HTMLMediaElement);
164
174
  if (this.podcast || this.live) {
165
175
  if (!this.downloadId) {
166
- this.loadDownloadId();
176
+ return;
167
177
  }
168
178
  if (
169
179
  this.live &&
@@ -214,21 +224,5 @@ export const playerLogic = defineComponent({
214
224
  }
215
225
  this.forceHide = true;
216
226
  },
217
- loadDownloadId(): void {
218
- if (!this.podcast) return;
219
- const matching_cookies = document.cookie
220
- .split(';')
221
- .map(item => {
222
- const _return = item.trim().split('=');
223
- return _return.map(item => item.trim());
224
- })
225
- .filter(item => {
226
- if(!this.podcast){return '';}
227
- return 'player_' + this.podcast.podcastId === item[0];
228
- });
229
- if (1 === matching_cookies.length) {
230
- this.setDownloadId(matching_cookies[0][1]);
231
- }
232
- },
233
227
  },
234
228
  })
@@ -16,6 +16,7 @@
16
16
  >
17
17
  <img
18
18
  v-lazy="participant.imageUrl"
19
+ :title="$t('Animator image')"
19
20
  class="img-box-circle mb-3"
20
21
  >
21
22
  <h2 class="text-capitalize">
package/src/locale/de.ts CHANGED
@@ -299,4 +299,5 @@ export default{
299
299
  'Podcast tags': "Podcast-Tags",
300
300
  "Enlarge":"Vergrößern",
301
301
  "Reduce":"Reduzieren",
302
+ "Use organization color":"Verwenden Sie die Organisationsfarbe",
302
303
  }
package/src/locale/en.ts CHANGED
@@ -300,4 +300,5 @@ export default{
300
300
  "You do not have the right to access this page":"You do not have the right to access this page",
301
301
  "Enlarge":"Enlarge",
302
302
  "Reduce":"Reduce",
303
+ "Use organization color":"Use organization color",
303
304
  };
package/src/locale/es.ts CHANGED
@@ -299,4 +299,5 @@ export default{
299
299
  'Podcast tags': 'Etiquetas de pódcast',
300
300
  "Enlarge":"Agrandar",
301
301
  "Reduce":"Reducir",
302
+ "Use organization color":"Usar el color de la organización",
302
303
  }
package/src/locale/fr.ts CHANGED
@@ -300,4 +300,5 @@ export default{
300
300
  "You do not have the right to access this page":"Vous n’avez pas le droit d’accéder à cette page",
301
301
  "Enlarge":"Agrandir",
302
302
  "Reduce":"Réduire",
303
+ "Use organization color":"Utiliser la couleur de l'organisation",
303
304
  };
package/src/locale/it.ts CHANGED
@@ -297,4 +297,5 @@ export default{
297
297
  'More episodes of this category : ': "Altri episodi su questo tema : {name}",
298
298
  "Enlarge":"Ingrandire",
299
299
  "Reduce":"Ridurre",
300
+ "Use organization color":"Usa il colore dell'organizzazione",
300
301
  };
package/src/locale/sl.ts CHANGED
@@ -299,4 +299,5 @@ export default{
299
299
  'Podcast tags': 'Oznake podkastov',
300
300
  "Enlarge":"Povečaj",
301
301
  "Reduce":"Zmanjšaj",
302
+ "Use organization color":"Uporabite barvo organizacije",
302
303
  }
@@ -6,6 +6,8 @@ $font-family-sans-serif: 'Roboto', sans-serif;
6
6
  $font-size-base: 0.9rem;
7
7
  $line-height-base: 1.6;
8
8
  $primary: #32815C;
9
+ $secondary: #636C73;
10
+ $danger: #E22C27;
9
11
 
10
12
  // Colors
11
13
  $blue: #3490dc;