@riosst100/pwa-marketplace 3.3.4 → 3.3.5

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.
package/i18n/id_ID.json CHANGED
@@ -546,5 +546,12 @@
546
546
  "messages.loginRequired": "Silakan masuk ke akun Anda untuk mengirim pesan ke seller.",
547
547
  "messages.messageSent": "Pesan berhasil dikirim.",
548
548
  "messages.replySent": "Balasan berhasil dikirim.",
549
- "messages.messageDeleted": "Pesan berhasil dihapus."
549
+ "messages.messageDeleted": "Pesan berhasil dihapus.",
550
+ "order.track.inTransit": "Sedang Dikirim",
551
+ "order.track.delivered": "Pesanan Tiba",
552
+ "order.track.courier": "Kurir",
553
+ "order.track.waybill": "Resi",
554
+ "order.track.status": "Status",
555
+ "order.track.step1": "Langkah 1 dari 2",
556
+ "order.track.step2": "Langkah 2 dari 2"
550
557
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@riosst100/pwa-marketplace",
3
3
  "author": "riosst100@gmail.com",
4
- "version": "3.3.4",
4
+ "version": "3.3.5",
5
5
  "main": "src/index.js",
6
6
  "pwa-studio": {
7
7
  "targets": {
@@ -18,6 +18,7 @@ const LoadingRequest = props => {
18
18
  const {
19
19
  isLoading
20
20
  } = props;
21
+ // const isLoading = true;
21
22
 
22
23
  const modal = isLoading ? (
23
24
  <div className={modalClasses.overlay} role="dialog" aria-modal="true" aria-label="Modal">
@@ -27,7 +28,8 @@ const LoadingRequest = props => {
27
28
  <img src={process.env.MAGENTO_BACKEND_URL + "/media/images/spinner2.gif"} style={{"width":"15%"}} />
28
29
  <div style={{
29
30
  "marginTop": "10px",
30
- "color": "white"
31
+ "color": "white",
32
+ "fontSize": "14px"
31
33
  }}>Please wait...</div>
32
34
  </div>
33
35
  </div>
@@ -75,8 +75,9 @@
75
75
  .backdrop{
76
76
  position:absolute;
77
77
  inset:0;
78
- background: rgba(0, 0, 0, 0.64);
79
- backdrop-filter: blur(8px);
78
+ /* background: rgba(0, 0, 0, 0.64); */
79
+ background: rgb(0 0 0 / 50%);
80
+ /* backdrop-filter: blur(1px); */
80
81
  }
81
82
  .dialog{
82
83
  position:relative;
@@ -119,6 +120,7 @@
119
120
  text-align: center;
120
121
  flex-wrap: wrap;
121
122
  align-content: center;
123
+ align-items: center;
122
124
  }
123
125
  .triggerBtn{
124
126
  background:#f76b1c;
@@ -0,0 +1,151 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import { TickCircle } from 'iconsax-react';
3
+ import { useIntl } from 'react-intl';
4
+
5
+ import useTrackOrder from '@riosst100/pwa-marketplace/src/talons/TrackOrder/useTrackOrder';
6
+
7
+ const TrackOrder = ({ order }) => {
8
+ const incrementId = useMemo(() => {
9
+ const num = order?.number;
10
+ return typeof num === 'number' ? String(num) : (num || '');
11
+ }, [order]);
12
+
13
+ const { trackOrder, isLoadingWithoutData, isBackgroundLoading, errorMessage } = useTrackOrder({ incrementId });
14
+
15
+ const { formatMessage, locale } = useIntl();
16
+
17
+ const status = (order?.status || '').toLowerCase();
18
+ const delivered = status === 'complete';
19
+ const canceled = status === 'canceled' || status === 'cancel' || status === 'cancelled';
20
+ const inTransit = !canceled && status !== '' && !delivered;
21
+
22
+ const summary = trackOrder?.summary;
23
+ const manifests = Array.isArray(trackOrder?.manifest)
24
+ ? trackOrder.manifest
25
+ : (trackOrder?.manifest ? [trackOrder.manifest] : []);
26
+
27
+ const activeManifestIndex = useMemo(() => {
28
+ if (!manifests.length) return -1;
29
+ let bestIdx = 0;
30
+ let bestTime = -Infinity;
31
+ manifests.forEach((m, idx) => {
32
+ const d = m?.manifest_date;
33
+ const t = m?.manifest_time;
34
+ const dt = d && t ? new Date(`${d}T${t}`) : undefined;
35
+ const time = dt && !isNaN(dt) ? dt.getTime() : NaN;
36
+ if (!isNaN(time) && time > bestTime) {
37
+ bestTime = time;
38
+ bestIdx = idx;
39
+ }
40
+ });
41
+ return isFinite(bestTime) ? bestIdx : 0;
42
+ }, [manifests]);
43
+
44
+ const formatManifestDate = m => {
45
+ const d = m?.manifest_date;
46
+ const t = m?.manifest_time;
47
+ if (d && t) {
48
+ const dt = new Date(`${d}T${t}`);
49
+ if (!isNaN(dt)) {
50
+ try {
51
+ return new Intl.DateTimeFormat(locale, { dateStyle: 'medium', timeStyle: 'short' }).format(dt);
52
+ } catch (e) {
53
+ return dt.toLocaleString();
54
+ }
55
+ }
56
+ }
57
+ return [t || '', d || ''].filter(Boolean).join(' ') || '-';
58
+ };
59
+
60
+ const [copied, setCopied] = useState(false);
61
+ const handleCopyWaybill = () => {
62
+ const text = summary?.waybill_number;
63
+ if (!text) return;
64
+ if (navigator?.clipboard?.writeText) {
65
+ navigator.clipboard.writeText(text)
66
+ .then(() => {
67
+ setCopied(true);
68
+ setTimeout(() => setCopied(false), 1500);
69
+ })
70
+ .catch(() => {});
71
+ }
72
+ };
73
+
74
+ const loading = isLoadingWithoutData || isBackgroundLoading;
75
+ return (
76
+ <div className="block-content rounded-md border border-gray-100 px-4 py-6 relative" aria-busy={loading}>
77
+ <div className="relative h-12 mb-6">
78
+ <div className="absolute left-0 right-0 top-[10px] flex">
79
+ <div className={`h-[2px] w-1/2 rounded-l-full ${inTransit || delivered ? 'bg-blue-600' : 'bg-gray-100'}`}></div>
80
+ <div className={`h-[2px] w-1/2 rounded-r-full ${delivered ? 'bg-blue-600' : 'bg-gray-100'}`}></div>
81
+ </div>
82
+ <div className="absolute left-0 right-0 top-[10px] flex justify-between">
83
+ <div className="flex flex-col items-center">
84
+ <span className={`w-3 h-3 rounded-full -translate-y-1/2 ring-2 ring-white shadow ${inTransit || delivered ? 'bg-blue-700' : 'bg-gray-100'} ${inTransit ? 'animate-pulse' : ''}`}></span>
85
+ <span className={`mt-2 text-sm ${inTransit || delivered ? 'text-blue-700 font-medium' : 'text-gray-200'}`}>{formatMessage({ id: 'order.track.inTransit', defaultMessage: 'In Transit' })}</span>
86
+ </div>
87
+ <div className="flex flex-col items-center">
88
+ <span className={`w-5 h-5 rounded-full -translate-y-1/2 flex items-center justify-center ring-2 ring-white shadow transition-transform duration-300 ${delivered ? 'bg-green-500 scale-110' : (delivered || inTransit ? 'bg-blue-700' : 'bg-gray-100')}`}>
89
+ {delivered && <TickCircle size={14} color="#ffffff" variant="Bold" />}
90
+ </span>
91
+ <span className={`mt-2 text-sm ${delivered ? 'text-green-600 font-medium' : 'text-gray-200'}`}>{formatMessage({ id: 'order.track.delivered', defaultMessage: 'Delivered' })}</span>
92
+ </div>
93
+ </div>
94
+ </div>
95
+
96
+ <div className="flex flex-col gap-2 mb-6">
97
+ {summary && (
98
+ <div className="flex flex-col gap-2">
99
+ <div className="px-2 py-1 rounded bg-gray-100 text-gray-900 text-xs w-[250px]">
100
+ <strong>{formatMessage({ id: 'order.track.courier', defaultMessage: 'Courier' })}:</strong> {summary.courier_name || '-'}
101
+ </div>
102
+ <div className="px-2 py-1 rounded bg-gray-100 text-gray-900 text-xs w-[250px] flex items-center justify-between">
103
+ <div className="truncate" title={summary.waybill_number || ''}>
104
+ <strong>{formatMessage({ id: 'order.track.waybill', defaultMessage: 'Waybill' })}:</strong> {summary.waybill_number || '-'}
105
+ </div>
106
+ {summary.waybill_number && (
107
+ <button type="button" onClick={handleCopyWaybill} className="ml-2 px-2 py-1 rounded bg-blue-600 text-white text-xs hover:bg-blue-700">
108
+ {copied ? formatMessage({ id: 'common.copied', defaultMessage: 'Copied!' }) : formatMessage({ id: 'common.copy', defaultMessage: 'Copy' })}
109
+ </button>
110
+ )}
111
+ </div>
112
+ <div className={`px-2 py-1 rounded text-xs w-[250px] ${summary.status?.toLowerCase() === 'delivered' ? 'bg-green-100 text-green-700' : 'bg-blue-100 text-blue-700'}`}>
113
+ <strong>{formatMessage({ id: 'order.track.status', defaultMessage: 'Status' })}:</strong> {summary.status || '-'}
114
+ </div>
115
+ </div>
116
+ )}
117
+ {errorMessage && (
118
+ <div className="text-red-600 text-sm">{errorMessage}</div>
119
+ )}
120
+ </div>
121
+
122
+ {manifests.length > 0 && (
123
+ <div className="relative flex flex-col gap-4">
124
+ <div className="pointer-events-none absolute left-[4px] top-0 bottom-0 border-l border-gray-100"></div>
125
+ {manifests.map((m, idx) => (
126
+ <div key={idx} className="flex items-start gap-3">
127
+ <div className={`mt-1 w-2 h-2 rounded-full z-10 ${idx === activeManifestIndex ? 'bg-blue-700' : 'bg-gray-100'}`}></div>
128
+ <div className="flex-1">
129
+ <div className="text-sm text-gray-800">{m?.manifest_description || '-'}</div>
130
+ {m?.city_name && (
131
+ <div className="text-xs text-gray-500">{m.city_name}</div>
132
+ )}
133
+ <div className="text-xs text-gray-500">{formatManifestDate(m)}</div>
134
+ </div>
135
+ </div>
136
+ ))}
137
+ </div>
138
+ )}
139
+ {loading && (
140
+ <div className="absolute inset-0 z-20 bg-white/60 backdrop-blur-[1px] flex items-center justify-center">
141
+ <div className="flex items-center gap-2">
142
+ <span className="w-5 h-5 rounded-full border-2 border-gray-200 border-t-blue-600 animate-spin"></span>
143
+ <span className="text-sm text-gray-700">{formatMessage({ id: 'order.track.loading', defaultMessage: 'Loading tracking order…' })}</span>
144
+ </div>
145
+ </div>
146
+ )}
147
+ </div>
148
+ );
149
+ };
150
+
151
+ export default TrackOrder;
@@ -5,6 +5,7 @@ import Button from '@magento/venia-ui/lib/components/Button';
5
5
  import cn from 'classnames';
6
6
  import { Printer, ConvertCard } from 'iconsax-react';
7
7
  import ItemsOrdered from './components/itemsOrdered';
8
+ import TrackOrder from './components/trackOrder';
8
9
  import RMAList from './components/rmaList';
9
10
  import useRmaPage from '@riosst100/pwa-marketplace/src/talons/RMAPage/useRmaPage';
10
11
  import { useLocation, useParams, useHistory } from 'react-router-dom';
@@ -93,6 +94,7 @@ const OrderDetail = (props) => {
93
94
  ? new Date(order.order_date.replace(' ', 'T')).toLocaleDateString(undefined, { year: 'numeric', month: 'long', day: 'numeric' })
94
95
  : '-';
95
96
  const status = order?.status || '-';
97
+ const isClosed = (order?.status || '').toLowerCase() === 'closed';
96
98
  const shippingMethod = order?.shipping_method || '-';
97
99
  const trackingNumber = order?.shipments?.[0]?.tracking?.[0]?.number || '-';
98
100
  const shippingAddress = order?.shipping_address;
@@ -236,6 +238,19 @@ const OrderDetail = (props) => {
236
238
  </div>
237
239
  </div>
238
240
  </div>
241
+ {!isClosed && (
242
+ <div className="block block-order-details-view">
243
+ <div aria-live="polite" className="text-lg font-medium text-left mb-4 block-title">
244
+ {
245
+ formatMessage({
246
+ id: 'order.trackOrder',
247
+ defaultMessage: 'Track Order'
248
+ })
249
+ }
250
+ </div>
251
+ <TrackOrder order={order} />
252
+ </div>
253
+ )}
239
254
  <div className="block block-order-details-view">
240
255
  <div aria-live="polite" className="text-lg font-medium text-left mb-4 block-title">
241
256
  {
@@ -158,6 +158,7 @@ export const useSignIn = props => {
158
158
  }
159
159
 
160
160
  setIsSigningIn(false);
161
+ setIsLoading(false);
161
162
  }
162
163
  },
163
164
  [
@@ -0,0 +1,24 @@
1
+ import { gql } from '@apollo/client';
2
+
3
+ export const GET_TRACK_ORDER = gql`
4
+ query GetTrackOrder($increment_id: String!) {
5
+ trackOrder(increment_id: $increment_id) {
6
+ delivered
7
+ summary {
8
+ courier_name
9
+ waybill_number
10
+ status
11
+ }
12
+ manifest {
13
+ manifest_description
14
+ manifest_date
15
+ city_name
16
+ manifest_time
17
+ }
18
+ }
19
+ }
20
+ `;
21
+
22
+ export default {
23
+ getTrackOrderQuery: GET_TRACK_ORDER
24
+ };
@@ -134,6 +134,7 @@ export const useSocialLogin = props => {
134
134
  }
135
135
 
136
136
  setIsSigningIn(false);
137
+ setIsLoading(false);
137
138
  }
138
139
  }
139
140
  }, [
@@ -0,0 +1,53 @@
1
+ import { useEffect, useMemo } from 'react';
2
+ import { useQuery } from '@apollo/client';
3
+
4
+ import { useAppContext } from '@magento/peregrine/lib/context/app';
5
+ import { deriveErrorMessage } from '@magento/peregrine/lib/util/deriveErrorMessage';
6
+
7
+ import operations from '@riosst100/pwa-marketplace/src/overwrites/peregrine/lib/talons/TrackOrder/trackOrder.gql';
8
+
9
+ export const useTrackOrder = (props = {}) => {
10
+ const { incrementId } = props;
11
+ const { getTrackOrderQuery } = operations;
12
+
13
+ const [
14
+ ,
15
+ {
16
+ actions: { setPageLoading }
17
+ }
18
+ ] = useAppContext();
19
+
20
+ const {
21
+ data,
22
+ error,
23
+ loading,
24
+ refetch
25
+ } = useQuery(getTrackOrderQuery, {
26
+ fetchPolicy: 'cache-and-network',
27
+ variables: { increment_id: incrementId },
28
+ skip: !incrementId
29
+ });
30
+
31
+ const isLoadingWithoutData = !data && loading;
32
+ const isBackgroundLoading = !!data && loading;
33
+
34
+ // Update the page indicator if the GraphQL query is in flight.
35
+ useEffect(() => {
36
+ setPageLoading(isBackgroundLoading);
37
+ }, [isBackgroundLoading, setPageLoading]);
38
+
39
+ const derivedErrorMessage = useMemo(
40
+ () => deriveErrorMessage([error]),
41
+ [error]
42
+ );
43
+
44
+ return {
45
+ errorMessage: derivedErrorMessage,
46
+ isLoadingWithoutData,
47
+ isBackgroundLoading,
48
+ trackOrder: data ? data.trackOrder : undefined,
49
+ refetchTrackOrder: refetch
50
+ };
51
+ };
52
+
53
+ export default useTrackOrder;