Skip to content

Commit c39a2a4

Browse files
committed
squash
1 parent 6b2516a commit c39a2a4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+2349
-37
lines changed

.env.example

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,23 @@ VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
239239
# VITE_LOCAL_DEV=true
240240
# VITE_HTTP_PROXY_TARGET=http://localhost:8000
241241

242-
# DISABLE_IMPORT_FROM_SERVER=false
242+
# DISABLE_IMPORT_FROM_SERVER=false
243+
244+
###################################################################
245+
# Payment integration (requires SE) #
246+
###################################################################
247+
248+
# MOLLIE_API_KEY=
249+
# STRIPE_API_KEY=
250+
251+
# https://github.com/thephpleague/omnipay-paypal/blob/master/src/RestGateway.php
252+
# PAYPAL_CLIENT_ID=
253+
# PAYPAL_SECRET=
254+
# PAYPAL_TEST_MODE=
255+
256+
# https://github.com/thephpleague/omnipay-paypal/blob/master/src/ExpressInContextGateway.php
257+
# https://github.com/thephpleague/omnipay-paypal/blob/master/src/ProGateway.php
258+
# PAYPAL_API_USERNAME=
259+
# PAYPAL_API_PASSWORD=
260+
# PAYPAL_API_SIGNATURE=
261+
# PAYPAL_TEST_MODE=

lang/en/webshop.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
return [
4+
/*
5+
|--------------------------------------------------------------------------
6+
| Webshop page
7+
|--------------------------------------------------------------------------
8+
*/
9+
"card" => [
10+
"cardHolder" => "Card Holder",
11+
"fullName" => "Full Name",
12+
"expires" => "Expires",
13+
"MM" => "MM",
14+
"YY" => "YY"
15+
16+
],
17+
"cardForm" => [
18+
"cardNumber" => "Card Number",
19+
"cardName" => "Card Name",
20+
"expirationDate" => "Expiration Date",
21+
"month" => "Month",
22+
"year" => "Year",
23+
"CVV" => "CVV",
24+
"submit" => "Submit",
25+
"invalidCardNumber" => "Invalid Card Number"
26+
]
27+
];

resources/js/components/drawers/AlbumEdit.vue

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,19 @@
4242
>
4343
{{ $t("gallery.album.tabs.move") }}
4444
</li>
45+
<li
46+
v-if="canManagePurchase"
47+
:class="{
48+
'px-2 pt-3 pb-4 cursor-pointer font-bold text-center transition-colors ease-in-out select-none': true,
49+
'xl:border-l-2': isLTR(),
50+
'xl:border-r-2': !isLTR(),
51+
'max-xl:border-b-2 border-solid border-primary-500 text-primary-500 hover:border-primary-300 hover:text-primary-300': true,
52+
'bg-primary-500/5': activeTab === 4,
53+
}"
54+
@click="activeTab = 4"
55+
>
56+
{{ $t("Shop management") }}
57+
</li>
4558
<li
4659
v-if="canDelete || canTransfer"
4760
:class="{
@@ -67,6 +80,9 @@
6780
<div v-if="activeTab === 2 && canMove" class="w-full xl:w-5/6 flex justify-center flex-wrap mb-4 sm:mt-7 ltr:pl-7 rtl:pr-7">
6881
<AlbumMove :key="`move_${albumStore.album?.id}`" />
6982
</div>
83+
<div v-if="activeTab === 4 && canManagePurchase" class="w-full xl:w-5/6 flex justify-center flex-wrap mb-4 sm:mt-7 ltr:pl-7 rtl:pr-7">
84+
<AlbumPurchasable :key="`purchasable_${albumStore.album?.id}`" />
85+
</div>
7086
<div
7187
v-if="activeTab === 3 && (canDelete || canTransfer)"
7288
class="w-full xl:w-5/6 flex justify-center flex-wrap mb-4 sm:mt-7 ltr:pl-7 rtl:pr-7"
@@ -84,6 +100,7 @@ import AlbumProperties from "@/components/forms/album/AlbumProperties.vue";
84100
import AlbumVisibility from "@/components/forms/album/AlbumVisibility.vue";
85101
import AlbumDelete from "@/components/forms/album/AlbumDelete.vue";
86102
import AlbumMove from "@/components/forms/album/AlbumMove.vue";
103+
import AlbumPurchasable from "@/components/forms/album/AlbumPurchasable.vue";
87104
import AlbumTransfer from "@/components/forms/album/AlbumTransfer.vue";
88105
import AlbumShare from "@/components/forms/album/AlbumShare.vue";
89106
import { storeToRefs } from "pinia";
@@ -108,6 +125,7 @@ const canShare = computed(() => albumStore.rights?.can_share_with_users && numUs
108125
const canMove = computed(() => albumStore.config?.is_model_album && albumStore.rights?.can_move);
109126
const canTransfer = computed(() => albumStore.config?.is_base_album && numUsers.value > 1 && albumStore.rights?.can_transfer);
110127
const canDelete = computed(() => albumStore.config?.is_base_album && albumStore.rights?.can_delete);
128+
const canManagePurchase = computed(() => albumStore.config?.is_model_album && albumStore.rights?.can_make_purchasable);
111129
112130
function close() {
113131
activeTab.value = 0;
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<template>
2+
<Card class="sm:p-4 xl:px-9 max-w-3xl w-full py-0" :pt:body:class="'p-0'">
3+
<template #content>
4+
<div v-if="albumPurchasable !== undefined" class="flex flex-col gap-4">
5+
<template v-if="albumPurchasable === null">
6+
<p class="font-bold text-muted-color text-lg text-center">This album is not purchasable (yet).</p>
7+
<Textarea v-model="description" placeholder="Description for clients" />
8+
<Textarea v-model="note" placeholder="Owner's Note" />
9+
<PricesInput :prices="prices" />
10+
<div class="flex gap-4">
11+
<Button
12+
icon="pi pi-plus"
13+
label="Set Purchasable"
14+
class="border-none w-full"
15+
@click="makePurchasable"
16+
:disabled="prices.length === 0"
17+
>
18+
</Button>
19+
<Button
20+
icon="pi pi-forward"
21+
severity="danger"
22+
label="Set Purchasable and propagate"
23+
class="font-bold w-full border-none"
24+
@click="
25+
appliesToSubalbums = true;
26+
makePurchasable();
27+
"
28+
:disabled="prices.length === 0"
29+
>
30+
</Button>
31+
</div>
32+
<Message severity="error" v-if="prices.length === 0">Set at least one price.</Message>
33+
</template>
34+
<template v-else>
35+
<Textarea v-model="description" placeholder="Description for clients" />
36+
<Textarea v-model="note" placeholder="Owner's Note" />
37+
<PricesInput :prices="prices" />
38+
<div class="flex gap-4">
39+
<Button
40+
class="text-danger-800 font-bold hover:text-white hover:bg-danger-800 w-full bg-transparent border-none"
41+
@click="disable"
42+
>Disable</Button
43+
>
44+
<Button class="border-none font-bold w-full" @click="makePurchasable" :disabled="prices.length === 0"> Update </Button>
45+
</div>
46+
<Message severity="error" v-if="prices.length === 0">Set at least one price.</Message>
47+
</template>
48+
</div>
49+
</template>
50+
</Card>
51+
</template>
52+
<script setup lang="ts">
53+
import ShopManagementService, { Price } from "@/services/shop-management-service";
54+
import Card from "primevue/card";
55+
import { useToast } from "primevue/usetoast";
56+
import { onMounted, ref } from "vue";
57+
import Textarea from "@/components/forms/basic/Textarea.vue";
58+
import Button from "primevue/button";
59+
import PricesInput from "@/components/forms/shop-management/PricesInput.vue";
60+
import Message from "primevue/message";
61+
import { useAlbumStore } from "@/stores/AlbumState";
62+
63+
const toast = useToast();
64+
const albumStore = useAlbumStore();
65+
66+
const albumPurchasable = ref<undefined | App.Http.Resources.Shop.EditablePurchasableResource | null>(undefined);
67+
68+
const description = ref<string | undefined>(undefined);
69+
const note = ref<string | undefined>(undefined);
70+
const appliesToSubalbums = ref<boolean>(false);
71+
const prices = ref<Price[]>([]);
72+
73+
function load() {
74+
if (!albumStore.albumId) {
75+
return;
76+
}
77+
78+
// Reset state
79+
appliesToSubalbums.value = false;
80+
81+
ShopManagementService.list([albumStore.albumId])
82+
.then((response) => {
83+
albumPurchasable.value = response.data.find((p) => p.album_id === albumStore.albumId && p.photo_id === null) ?? null;
84+
85+
description.value = albumPurchasable.value?.description ?? undefined;
86+
note.value = albumPurchasable.value?.owner_notes ?? undefined;
87+
prices.value =
88+
albumPurchasable.value?.prices?.map((p: App.Http.Resources.Shop.PriceResource) => {
89+
return { price: p.price_cents, license_type: p.license_type, size_variant_type: p.size_variant };
90+
}) ?? [];
91+
})
92+
.catch((error) => {
93+
toast.add({ severity: "error", summary: "Error", detail: error.message, life: 3000 });
94+
});
95+
}
96+
97+
function makePurchasable() {
98+
if (!albumStore.albumId) {
99+
return;
100+
}
101+
102+
ShopManagementService.createPurchasableAlbum({
103+
album_ids: [albumStore.albumId],
104+
note: note.value ?? null,
105+
description: description.value ?? null,
106+
prices: prices.value,
107+
applies_to_subalbums: appliesToSubalbums.value,
108+
})
109+
.then(() => {
110+
toast.add({ severity: "success", summary: "Success", detail: "Album is now purchasable", life: 3000 });
111+
load();
112+
})
113+
.catch((error) => {
114+
toast.add({ severity: "error", summary: "Error", detail: error.message, life: 3000 });
115+
});
116+
}
117+
118+
function disable() {
119+
if (albumPurchasable.value === null || albumPurchasable.value === undefined) {
120+
return;
121+
}
122+
ShopManagementService.deletePurchasable(albumPurchasable.value.purchasable_id)
123+
.then(() => {
124+
toast.add({ severity: "success", summary: "Success", detail: "Album is no longer purchasable", life: 3000 });
125+
load();
126+
})
127+
.catch((error) => {
128+
toast.add({ severity: "error", summary: "Error", detail: error.message, life: 3000 });
129+
});
130+
}
131+
132+
onMounted(() => {
133+
load();
134+
});
135+
</script>

resources/js/components/forms/basic/Fieldset.vue

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
</template>
1212
<script setup lang="ts">
1313
import Fieldset from "primevue/fieldset";
14-
import { defineProps } from "vue";
1514
1615
const props = defineProps<{
1716
legend?: string;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<template>
2+
<InputNumber
3+
v-model="modelValue"
4+
placeholder="Price"
5+
inputId="currency-us"
6+
mode="currency"
7+
:currency="props.currency"
8+
locale="en-US"
9+
class="border-0 w-full border-b hover:border-b-primary-400 focus:border-b-primary-400 p-inputcurrency"
10+
@update:model-value="updateValue"
11+
@blur="updateValue"
12+
/>
13+
</template>
14+
<script setup lang="ts">
15+
import { onMounted, ref, watch } from "vue";
16+
import InputNumber from "primevue/inputnumber";
17+
18+
const emits = defineEmits(["update:modelValue"]);
19+
20+
const props = defineProps<{
21+
value: number | null;
22+
currency: string;
23+
}>();
24+
25+
// We devide by 100 to convert cents to currency units.
26+
const modelValue = ref(0);
27+
28+
function updateValue() {
29+
// We multiply by 100 to convert currency units to cents.
30+
const newValue = modelValue.value !== null ? Math.round(modelValue.value * 100) : null;
31+
emits("update:modelValue", newValue);
32+
}
33+
34+
onMounted(() => {
35+
modelValue.value = (props.value ?? 0) / 100;
36+
});
37+
38+
watch(
39+
() => props.value,
40+
(newValue: number | null) => {
41+
modelValue.value = (newValue ?? 0) / 100;
42+
},
43+
);
44+
</script>
45+
<style lang="css">
46+
.p-inputcurrency input {
47+
border: none;
48+
}
49+
</style>

0 commit comments

Comments
 (0)