Skip to content

Commit da6a14e

Browse files
committed
Merge branch 'release/2.34.0' into main
2 parents dd870dc + 21c73ea commit da6a14e

File tree

12 files changed

+414
-34
lines changed

12 files changed

+414
-34
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"version": "2.33.2",
2+
"version": "2.34.0",
33
"name": "shieldmaiden",
44
"description": "A Dungeons and Dragons Combat Tracker",
55
"productName": "Shieldmaiden",

src/components/PromoBanner.vue

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,22 @@
1212
</button>
1313
<div class="promo-banner__body">
1414
<div class="discount">
15-
<div class="discount__percentage">{{ discount }}</div>
15+
<div class="discount__percentage">{{ active_promotion.discount }}</div>
1616
<div class="discount__off">OFF</div>
1717
</div>
1818
<div>
19-
<div class="code">
19+
<div class="code" @click="copyCode">
2020
Use promo code
21-
<div>{{ code }}</div>
21+
<div>
22+
{{ active_promotion.code }}
23+
<hk-icon icon="fas fa-copy" class="ml-1" />
24+
</div>
2225
</div>
2326
</div>
2427
</div>
2528
<div class="promo-banner__footer">
2629
<div class="remaining">
27-
Get your first month with a <strong>{{ discount }}%</strong> discount.
30+
Get your first month with a <strong>{{ active_promotion.discount }}%</strong> discount.
2831
<template v-if="hours_remaining <= 1">Less than </template>
2932
<span class="remaining__count">{{
3033
days_remaining ? days_remaining : hours_remaining
@@ -38,6 +41,7 @@
3841

3942
<script>
4043
import { mapGetters } from "vuex";
44+
import { promotionService } from "src/services/promotions";
4145
4246
export default {
4347
name: "PromoBanner",
@@ -49,50 +53,50 @@ export default {
4953
},
5054
data() {
5155
return {
56+
active_promotion: undefined,
5257
code: "SHIELDMAIDEN80",
53-
discount: 80,
5458
now: new Date(),
55-
start: new Date("2024-11-15T00:00:00Z"),
56-
end: new Date("2024-12-03T07:59:59Z"),
5759
showSetter: undefined,
5860
timer: null,
5961
};
6062
},
6163
computed: {
6264
...mapGetters(["tier"]),
63-
show_banner: {
64-
get() {
65-
const show =
66-
(this.tier?.price === "Free" || !this.tier) &&
67-
this.now >= this.start &&
68-
this.now <= this.end;
69-
return this.showSetter !== undefined ? this.showSetter : show;
70-
},
71-
set(newVal) {
72-
this.showSetter = newVal;
73-
},
65+
show_banner() {
66+
return this.active_promotion && (this.tier?.price === "Free" || !this.tier);
7467
},
7568
days_remaining() {
76-
const diff = this.end - this.now;
69+
const diff = this.active_promotion.active_until - this.now;
7770
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
7871
return days >= 1 ? days : undefined;
7972
},
8073
hours_remaining() {
81-
const diff = this.end - this.now;
74+
const diff = this.active_promotion.active_until - this.now;
8275
const hours = Math.floor(diff / (1000 * 60 * 60));
8376
return hours;
8477
},
8578
},
8679
methods: {
80+
copyCode(event) {
81+
event.preventDefault();
82+
navigator.clipboard.writeText(this.active_promotion.code);
83+
this.$snotify.success("To clipboard", "Code Copied!", {
84+
position: "rightTop",
85+
});
86+
},
87+
async getActivePromotion() {
88+
return await promotionService.getFirstActivePromotion();
89+
},
8790
purchaseEvent() {
8891
this.$gtm.trackEvent({
8992
event: "purchase",
9093
});
9194
},
9295
},
93-
mounted() {
94-
if (this.show_banner) {
95-
this.$emit("discount", this.discount);
96+
async mounted() {
97+
this.active_promotion = await this.getActivePromotion();
98+
if (this.show_banner && this.active_promotion) {
99+
this.$emit("discount", this.active_promotion.discount);
96100
}
97101
this.timer = setInterval(() => {
98102
this.now = new Date();
@@ -137,6 +141,9 @@ export default {
137141
138142
.code {
139143
color: $neutral-2;
144+
:hover {
145+
color: $primary;
146+
}
140147
141148
> div {
142149
color: $white;

src/components/Tiers.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
>
3434
<em v-if="t.price === 'Free'" class="neutral-2 sub">forever</em>
3535
<em v-else class="neutral-2 sub">{{
36-
discount ? "the first month" : "per month"
36+
discount && !annually ? "the first month" : "per month"
3737
}}</em>
3838
</div>
3939
<ul>

src/components/campaign/Players.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<tag :is="cardView ? 'hk-card' : 'div'" :class="!cardView ? 'normal-view' : ''">
3-
<div slot="header" class="pane__header top-menu">
3+
<div slot="header" class="pane__header top-menu" :class="cardView && 'card-header'">
44
<div
55
class="money"
66
:class="{ red: currency >= maxCurrencyAmount }"
@@ -10,7 +10,7 @@
1010
show: true,
1111
type: 'drawers/party/Currency',
1212
data: { current: currency },
13-
})
13+
})
1414
: null
1515
"
1616
>

src/components/combat/Finished.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@
116116
</div>
117117

118118
<!-- CURRENCY -->
119-
<div class="currency mb-3" v-if="encounter.currency">
119+
<div class="currency mb-3">
120120
<div class="currency-form">
121121
<div v-for="(coin, key) in currencies" :key="key">
122122
<img :src="require(`src/assets/_img/currency/${coin.color}.svg`)" />
@@ -235,7 +235,7 @@ export default {
235235
campaign: {},
236236
patreon: true,
237237
tab: "loot",
238-
editableEncounter: this.encounter,
238+
editableEncounter: { ...this.encounter, currency: {} },
239239
};
240240
},
241241
computed: {

src/components/combat/actions/Roll.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div v-if="current" tabindex="-1">
44
<h3 v-if="targeted.length === 0" class="red text-center">Select one or more targets</h3>
55
<template v-else-if="current.entityType !== 'player'">
6-
<template v-if="['npc', 'environment'].includes(current.entityType)">
6+
<template v-if="['npc', 'environment', 'companion'].includes(current.entityType)">
77
<!-- ACTIONS -->
88
<q-tabs
99
v-model="tab"
@@ -95,14 +95,14 @@
9595
action.recharge === "rest"
9696
? "after a Short or Long Rest"
9797
: action.recharge
98-
})`
98+
})`
9999
: ``
100100
}}
101101
{{
102102
action.limit
103103
? `(${action.limit}/${
104104
action.limit_type ? action.limit_type.capitalize() : `Day`
105-
})`
105+
})`
106106
: ``
107107
}}
108108
{{

src/css/styles.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ html body {
3535

3636
button {
3737
all: unset;
38+
text-align: center;
3839
box-sizing: border-box;
3940
}
4041

src/router/routes.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,24 @@ const routes = [
10521052
},
10531053
],
10541054
},
1055+
{
1056+
path: "promotions",
1057+
component: {
1058+
render(c) {
1059+
return c("router-view");
1060+
},
1061+
},
1062+
meta: {
1063+
title: "Promotions",
1064+
},
1065+
children: [
1066+
{
1067+
path: "",
1068+
name: "Promotions",
1069+
component: () => import("src/views/Admin/Promotions.vue"),
1070+
},
1071+
],
1072+
},
10551073
{
10561074
path: "subscriptions",
10571075
component: {

src/services/promotions.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import firebase from "firebase/app";
2+
import { db } from "src/firebase";
3+
import { serverUtils } from "src/services/serverUtils";
4+
5+
const PROMOTION_REF = db.ref("promotions");
6+
const TIERS_REF = db.ref("tiers");
7+
8+
const START_TZ = "Z"; // UTC
9+
const END_TZ = "-07:00"; // PACIFIC TIME
10+
11+
export class promotionService {
12+
static async getAllPromotions() {
13+
return (await PROMOTION_REF.once("value")).val();
14+
}
15+
16+
static getPromotionsWithCallback(callback) {
17+
return PROMOTION_REF.on("value", callback);
18+
}
19+
20+
static async getAllActivePromotions() {
21+
const promotions = (await PROMOTION_REF.once("value")).val();
22+
const server_time = await serverUtils.getServerTime();
23+
return Object.values(promotions).filter((promotion) => {
24+
promotion.active_from = new Date(`${promotion.active_from}T00:00:00${START_TZ}`);
25+
promotion.active_until = new Date(`${promotion.active_until}T00:00:00${END_TZ}`);
26+
return (
27+
promotion.disabled === undefined &&
28+
server_time < promotion.active_until &&
29+
server_time > promotion.active_from
30+
);
31+
});
32+
}
33+
34+
static async getFirstActivePromotion() {
35+
const promotions = await this.getAllActivePromotions();
36+
return promotions.length > 0 ? promotions[0] : undefined;
37+
}
38+
39+
static async addNewPromotion(promotion_object) {
40+
promotion_object.code = promotion_object.code.toUpperCase();
41+
return PROMOTION_REF.child(promotion_object.code).set(promotion_object);
42+
}
43+
44+
static async deletePromotion(promotion_code) {
45+
return PROMOTION_REF.child(promotion_code).remove();
46+
}
47+
48+
static async disablePromotion(promotion_code) {
49+
return PROMOTION_REF.child(promotion_code).child("disabled").set(true);
50+
}
51+
52+
static async enablePromotion(promotion_code) {
53+
return PROMOTION_REF.child(promotion_code).child("disabled").remove();
54+
}
55+
}

0 commit comments

Comments
 (0)