@akinon/pz-b2b 1.18.0

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.
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+
3
+ import { GTMEvent, Product } from '@akinon/next/types';
4
+
5
+ export const pushCartView = (
6
+ products: Pick<Product, 'name' | 'pk' | 'price'>[]
7
+ ) => {
8
+ pushEvent({
9
+ Action: 'Checkout',
10
+ Label: 'Sepet',
11
+ ecommerce: {
12
+ products: products?.map((product) => ({
13
+ name: product.name,
14
+ id: product.pk,
15
+ price: product.price
16
+ }))
17
+ },
18
+ event: 'eeCheckout'
19
+ });
20
+ };
21
+
22
+ export const pushEvent = (event: GTMEvent) => {
23
+ const { Category = 'Enhanced Ecommerce', Value = 0, ...rest } = event;
24
+
25
+ window.dataLayer.push({ Category, Value, ...rest });
26
+ };
@@ -0,0 +1,144 @@
1
+ 'use client';
2
+
3
+ import { useLocalization } from '@akinon/next/hooks';
4
+ import { Image, Price, Input, Icon } from '@akinon/next/components';
5
+ import { useUpdateProductMutation } from '@akinon/next/data/client/b2b';
6
+ import { BasketItemType } from '@akinon/next/types';
7
+ import React, { useState } from 'react';
8
+
9
+ interface Props {
10
+ basketItem: BasketItemType;
11
+ setProductToBeDeleted: (item: BasketItemType) => void;
12
+ setRemoveProduct: (open: boolean) => void;
13
+ }
14
+
15
+ export const BasketItem = (props: Props) => {
16
+ const { t } = useLocalization();
17
+ const { basketItem, setRemoveProduct, setProductToBeDeleted } = props;
18
+ const [updateProduct] = useUpdateProductMutation();
19
+ const [divisionErrors, setDivisionErrors] = useState([]);
20
+
21
+ const handleChange = (divisionID, e, index) => {
22
+ if (parseInt(e.target.value) < 1) {
23
+ setRemoveProduct(true);
24
+ setProductToBeDeleted(basketItem);
25
+ } else {
26
+ updateProduct({
27
+ product_remote_id: basketItem.product_remote_id,
28
+ division: divisionID,
29
+ quantity: parseInt(e.target.value)
30
+ }).then((res) => {
31
+ if ('error' in res && 'data' in res.error) {
32
+ setDivisionErrors((prevErrors) => {
33
+ const newErrors = [...prevErrors];
34
+ newErrors[index] = (res.error as any).data.quantity || [];
35
+ return newErrors;
36
+ });
37
+ } else {
38
+ setDivisionErrors((prevErrors) => {
39
+ const newErrors = [...prevErrors];
40
+ newErrors[index] = [];
41
+ return newErrors;
42
+ });
43
+ }
44
+ });
45
+ }
46
+ };
47
+
48
+ return (
49
+ <>
50
+ <tr>
51
+ <td>
52
+ <div className='h-[183px] w-full flex flex-col justify-between items-center text-xs'>
53
+ <div className='w-full flex items-center justify-center py-3 mb-3 border-b'>
54
+ <Icon name='chevron-down' size={10} />
55
+ </div>
56
+
57
+ <div className='h-full overflow-hidden'
58
+ style={{ writingMode: 'vertical-rl', textOrientation: 'mixed', display: '-webkit-box', lineClamp: 1, WebkitLineClamp: 1, WebkitBoxOrient: 'vertical' }}
59
+ >
60
+ {basketItem.product.name}{basketItem.product.name}{basketItem.product.name}
61
+ </div>
62
+
63
+ <button
64
+ onClick={() => { setProductToBeDeleted(basketItem), setRemoveProduct(true) }}
65
+ className='py-1.5 border-t w-full flex items-center justify-center'
66
+ style={{ writingMode: 'vertical-rl', textOrientation: 'mixed' }}
67
+ >
68
+ {t('basket.b2b.table.delete')}
69
+ </button>
70
+ </div>
71
+ </td>
72
+
73
+ <td className='border text-center'>
74
+ <div className='min-w-[80px] w-full h-full flex items-center justify-center'>
75
+ <Image
76
+ src={basketItem.product.product_image}
77
+ alt={basketItem.product.name}
78
+ width={50}
79
+ height={65}
80
+ />
81
+ </div>
82
+ </td>
83
+
84
+ <td className='min-w-[181px] text-sm border text-[#3f4b5c] leading-normal'>
85
+ <div className='px-4'>
86
+ <div className='break-words'>{basketItem.product.name}</div>
87
+ <div className='break-words'>SKU: {basketItem.product.sku}</div>
88
+ </div>
89
+ </td>
90
+
91
+ <td className='text-sm border text-[#3f4b5c] leading-normal text-right px-3'>
92
+ <Price value={basketItem.price} className='w-max block' />
93
+ </td>
94
+
95
+ <td className='text-sm border text-[#3f4b5c] leading-normal text-center'>
96
+ <div className='min-w-[90px]'>
97
+ {basketItem.quantity}
98
+ </div>
99
+ </td>
100
+
101
+ <td className='border'>
102
+ <div className='min-w-[140px]'>
103
+ {basketItem.divisions.map((division) => (
104
+ <div className='py-5 px-1 border-b last:border-0 text-center flex items-center justify-center gap-x-1' key={division.name}>
105
+ <Icon name='b2b-info' size={18} className='text-[#293245]' />
106
+
107
+ <div className='text-sm underline text-[#3f4b5c] overflow-hidden' style={{ display: '-webkit-box', lineClamp: 1, WebkitLineClamp: 1, WebkitBoxOrient: 'vertical' }}>
108
+ {division.name}
109
+ </div>
110
+ </div>
111
+ ))}
112
+ </div>
113
+ </td>
114
+
115
+ <td className='border'>
116
+ <div className='min-w-[120px]'>
117
+ {basketItem.divisions.map((division, index) => (
118
+ <div className='relative py-5 border-b last:border-0 text-center flex flex-col gap-y-1 items-center justify-center' key={`division-${division.id}`}>
119
+ <Input
120
+ className='appearance-none outline-none w-20 h-6 px-2 py-0 border border-[#c8daec] rounded text-right'
121
+ type="number"
122
+ value={division.quantity}
123
+ onChange={(e) => handleChange(division.id, e, index)}
124
+ />
125
+
126
+ {divisionErrors[index] && (
127
+ <div className="text-xs text-error mb-2 absolute -bottom-1">
128
+ {divisionErrors[index].map((err) => (
129
+ <div key={err}>{err}</div>
130
+ ))}
131
+ </div>
132
+ )}
133
+ </div>
134
+ ))}
135
+ </div>
136
+ </td>
137
+
138
+ <td className='text-sm border text-[#3f4b5c] leading-normal text-right px-6'>
139
+ <Price value={basketItem.total_amount} className='w-max block' />
140
+ </td>
141
+ </tr>
142
+ </>
143
+ );
144
+ };
@@ -0,0 +1,4 @@
1
+ export * from './basket-item';
2
+ export * from './remove-product-modal';
3
+ export * from './request-quote-modal';
4
+ export * from './save-cart-modal';
@@ -0,0 +1,109 @@
1
+ 'use client';
2
+
3
+ import { useDeleteProductMutation } from '@akinon/next/data/client/b2b';
4
+ import { Image } from '@akinon/next/components';
5
+ import { useLocalization } from '@akinon/next/hooks';
6
+ import React, { useEffect } from 'react';
7
+ import { BasketItemType } from '@akinon/next/types';
8
+ import { useAddFavoriteMutation } from '@akinon/next/data/client/wishlist';
9
+ import clsx from 'clsx';
10
+
11
+ export const RemoveProductModal = ({
12
+ open,
13
+ setOpen,
14
+ basketItem
15
+ }: {
16
+ open: boolean;
17
+ setOpen: (open: boolean) => void;
18
+ basketItem: BasketItemType
19
+ }) => {
20
+ const { t } = useLocalization();
21
+ const [addFavorite] = useAddFavoriteMutation();
22
+ const [deleteProduct, { isLoading, isSuccess }] = useDeleteProductMutation();
23
+
24
+ const handleClick = (addToFavorite:boolean) => {
25
+ if(addToFavorite) {
26
+ addFavorite(basketItem.product_remote_id)
27
+ }
28
+
29
+ deleteProduct({ product_remote_id: basketItem.product_remote_id });
30
+ };
31
+
32
+ useEffect(() => {
33
+ if (isSuccess) {
34
+ setOpen(false);
35
+ }
36
+ }, [isSuccess]);
37
+
38
+ if (!open) return null;
39
+
40
+ return (
41
+ <div className="before:w-full before:h-screen before:content-[''] before:bg-black/60 before:z-40 before:fixed before:inset-0">
42
+ <div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
43
+ <header className="relative border-b border-[#d5dadb]">
44
+ <h3 className="text-left px-[30px] py-4 font-medium text-[17px]">
45
+ {t('basket.b2b.remove_product_modal.title')}
46
+ </h3>
47
+
48
+ <button
49
+ className="absolute right-[30px] top-5"
50
+ onClick={() => setOpen(false)}
51
+ >
52
+ <i className="pz-icon-close"></i>
53
+ </button>
54
+ </header>
55
+
56
+ <p className="text-left px-[30px] py-4 text-sm">
57
+ {t('basket.b2b.remove_product_modal.description')}
58
+ </p>
59
+
60
+ <div className="px-[30px] pb-5">
61
+ <div className='flex items-center gap-x-3'>
62
+ <div>
63
+ <Image src={basketItem.product.product_image} width={72} height={93} alt={basketItem.product.name} />
64
+ </div>
65
+ <div className='text-[#3f4b5c] text-sm'>
66
+ <p>{basketItem.product.name}</p>
67
+ <p>SKU: {basketItem.product.sku}</p>
68
+ </div>
69
+ </div>
70
+
71
+ <div className='flex gap-y-3 flex-col items-center'>
72
+ <button
73
+ type="submit"
74
+ className="h-8 bg-black text-white font-medium text-sm w-full"
75
+ disabled={isLoading}
76
+ onClick={() => handleClick(true)}
77
+ >
78
+ {isLoading ? (
79
+ <div className="w-full h-full flex justify-center items-center">
80
+ <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
81
+ </div>
82
+ ) : (
83
+ t('basket.b2b.remove_product_modal.add_to_favorites_button')
84
+ )}
85
+ </button>
86
+
87
+ <button
88
+ type="submit"
89
+ className={clsx(
90
+ 'h-auto bg-transparent text-[#181818] border-[#181818] text-xs w-auto',
91
+ !isLoading && 'border-b'
92
+ )}
93
+ disabled={isLoading}
94
+ onClick={() => handleClick(false)}
95
+ >
96
+ {isLoading ? (
97
+ <div className="w-full h-full flex justify-center items-center">
98
+ <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
99
+ </div>
100
+ ) : (
101
+ t('basket.b2b.remove_product_modal.delete_product_button')
102
+ )}
103
+ </button>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+ );
109
+ }
@@ -0,0 +1,97 @@
1
+ 'use client';
2
+
3
+ import { useCreateQuotationMutation } from '@akinon/next/data/client/b2b';
4
+ import { useLocalization } from '@akinon/next/hooks';
5
+ import React, { useEffect, useState } from 'react';
6
+
7
+ export const RequestQuoteModal = ({
8
+ open,
9
+ setOpen
10
+ }: {
11
+ open: boolean;
12
+ setOpen: (open: boolean) => void;
13
+ }) => {
14
+ const { t } = useLocalization();
15
+ const [createQuotation, { isLoading, isSuccess, error, isError }] = useCreateQuotationMutation();
16
+ const [errors, setErrors] = useState([]);
17
+ const [cartName, setCartName] = useState<string>('');
18
+
19
+ const handleSubmit = (e) => {
20
+ e.preventDefault();
21
+
22
+ if (!cartName) {
23
+ return;
24
+ }
25
+
26
+ createQuotation({ name: cartName }).then((res) => {
27
+ if ('error' in res && 'data' in res.error) {
28
+ setErrors((res.error.data as any).non_field_errors || []);
29
+ }
30
+ })
31
+ };
32
+
33
+ useEffect(() => {
34
+ if (isSuccess) {
35
+ setOpen(false);
36
+ }
37
+ }, [isSuccess]);
38
+
39
+ if (!open) return null;
40
+
41
+ return (
42
+ <div className="before:w-full before:h-screen before:content-[''] before:bg-black/60 before:z-40 before:fixed before:inset-0">
43
+ <div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
44
+ <header className="relative border-b border-[#d5dadb]">
45
+ <h3 className="text-left px-[30px] py-4 font-medium text-lg">
46
+ {t('basket.b2b.request_quote_modal.title')}
47
+ </h3>
48
+
49
+ <button
50
+ className="absolute right-[30px] top-5"
51
+ onClick={() => setOpen(false)}
52
+ >
53
+ <i className="pz-icon-close"></i>
54
+ </button>
55
+ </header>
56
+
57
+ <div className="p-[30px]">
58
+ <form onSubmit={(e) => handleSubmit(e)}>
59
+ <label htmlFor="cart-name-input" className="text-xs block mb-2">
60
+ {t('basket.b2b.request_quote_modal.quotation_name')}
61
+ </label>
62
+
63
+ <input
64
+ id="cart-name-input"
65
+ name="name"
66
+ type="text"
67
+ className="h-8 border border-[#eeeeee] text-sm mb-2.5 outline-none px-2 w-full"
68
+ onChange={(e) => setCartName(e.target.value)}
69
+ />
70
+
71
+ {isError && (
72
+ <div className="text-xs text-error mb-2">
73
+ {errors.map((err) => (
74
+ <div key={err}>{err}</div>
75
+ ))}
76
+ </div>
77
+ )}
78
+
79
+ <button
80
+ type="submit"
81
+ className="h-8 bg-black text-white font-medium text-sm w-full"
82
+ disabled={isLoading}
83
+ >
84
+ {isLoading ? (
85
+ <div className="w-full h-full flex justify-center items-center">
86
+ <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
87
+ </div>
88
+ ) : (
89
+ t('basket.b2b.request_quote_modal.button')
90
+ )}
91
+ </button>
92
+ </form>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ );
97
+ }
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import { useSaveBasketMutation } from '@akinon/next/data/client/b2b';
4
+ import { useLocalization } from '@akinon/next/hooks';
5
+ import React, { useEffect, useState } from 'react';
6
+
7
+ export const SaveCartModal = ({
8
+ open,
9
+ setOpen
10
+ }: {
11
+ open: boolean;
12
+ setOpen: (open: boolean) => void;
13
+ }) => {
14
+ const { t } = useLocalization();
15
+ const [cartName, setCartName] = useState<string>('');
16
+ const [saveBasket, { isLoading, isSuccess }] = useSaveBasketMutation();
17
+
18
+ const handleSubmit = (e) => {
19
+ e.preventDefault();
20
+
21
+ if (!cartName) {
22
+ return;
23
+ }
24
+
25
+ saveBasket({ name: cartName });
26
+ };
27
+
28
+ useEffect(() => {
29
+ if (isSuccess) {
30
+ setOpen(false);
31
+ }
32
+ }, [isSuccess]);
33
+
34
+ if (!open) return null;
35
+
36
+ return (
37
+ <div className="before:w-full before:h-screen before:content-[''] before:bg-black/60 before:z-40 before:fixed before:inset-0">
38
+ <div className="w-11/12 max-w-[344px] md:w-[370px] left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 fixed bg-white">
39
+ <header className="relative border-b border-[#d5dadb]">
40
+ <h3 className="text-left px-[30px] py-4 font-medium text-lg">
41
+ {t('basket.b2b.save_cart_modal.title')}
42
+ </h3>
43
+ <button
44
+ className="absolute right-[30px] top-5"
45
+ onClick={() => setOpen(false)}
46
+ >
47
+ <i className="pz-icon-close"></i>
48
+ </button>
49
+ </header>
50
+ <div className="p-[30px]">
51
+ <form onSubmit={(e) => handleSubmit(e)}>
52
+ <label htmlFor="cart-name-input" className="text-xs block mb-2">
53
+ {t('basket.b2b.save_cart_modal.cart_name')}
54
+ </label>
55
+ <input
56
+ id="cart-name-input"
57
+ name="name"
58
+ type="text"
59
+ className="h-8 border border-[#eeeeee] text-sm mb-2.5 outline-none px-2 w-full"
60
+ onChange={(e) => setCartName(e.target.value)}
61
+ />
62
+ <button
63
+ type="submit"
64
+ className="h-8 bg-black text-white font-medium text-sm w-full"
65
+ disabled={isLoading}
66
+ >
67
+ {isLoading ? (
68
+ <div className="w-full h-full flex justify-center items-center">
69
+ <div className="w-4 h-4 border-2 border-gray-600 border-t-transparent rounded-full animate-spin" />
70
+ </div>
71
+ ) : (
72
+ t('basket.b2b.save_cart_modal.save_cart_button')
73
+ )}
74
+ </button>
75
+ </form>
76
+ </div>
77
+ </div>
78
+ </div>
79
+ );
80
+ }
@@ -0,0 +1,184 @@
1
+ 'use client';
2
+
3
+ import { GetQuotationsResponse } from '@akinon/next/data/client/account';
4
+ import clsx from 'clsx';
5
+ import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
6
+ import { twMerge } from 'tailwind-merge';
7
+
8
+ type TabsType = {
9
+ quotations: GetQuotationsResponse | undefined;
10
+ setActiveTab: Dispatch<SetStateAction<string>>;
11
+ activeTab: string;
12
+ };
13
+
14
+ const initialTabCountsState = {
15
+ pendingCount: 0,
16
+ rejectedCount: 0,
17
+ approvedCount: 0,
18
+ sendingApprovalRequestCount: 0
19
+ };
20
+
21
+ export function Tabs(props: TabsType) {
22
+ const quotations = props.quotations?.results;
23
+ const setActiveTab = props.setActiveTab;
24
+ const activeTab = props.activeTab;
25
+ const [tabCounts, setTabCounts] = useState(initialTabCountsState);
26
+
27
+ useEffect(() => {
28
+ setTabCounts(initialTabCountsState);
29
+
30
+ quotations?.forEach((quotation) => {
31
+ if (quotation.status === 'pending') {
32
+ setTabCounts((prevCount) => ({
33
+ ...prevCount,
34
+ pendingCount: prevCount.pendingCount + 1
35
+ }));
36
+ } else if (quotation.status === 'rejected') {
37
+ setTabCounts((prevCount) => ({
38
+ ...prevCount,
39
+ rejectedCount: prevCount.rejectedCount + 1
40
+ }));
41
+ } else if (quotation.status === 'approved') {
42
+ setTabCounts((prevCount) => ({
43
+ ...prevCount,
44
+ approvedCount: prevCount.approvedCount + 1
45
+ }));
46
+ } else if (quotation.status === 'sending_approval_request') {
47
+ setTabCounts((prevCount) => ({
48
+ ...prevCount,
49
+ sendingApprovalRequestCount: prevCount.sendingApprovalRequestCount + 1
50
+ }));
51
+ }
52
+ });
53
+ }, [quotations]);
54
+
55
+ return (
56
+ <div className="mb-9 mt-4 lg:mt-0">
57
+ <div className="relative flex justify-start lg:justify-between space-x-3 lg:space-x-4">
58
+ <div
59
+ className="flex flex-col items-center space-y-1.5 transition-all cursor-pointer"
60
+ onClick={() => setActiveTab('all')}
61
+ >
62
+ <span
63
+ className={twMerge(
64
+ clsx(
65
+ 'flex items-center text-xl justify-center w-9 h-9 rounded-full p-1.5 border border-[#eeeded] text-black bg-white'
66
+ ),
67
+ activeTab === 'all' && 'bg-[#71d200] text-white'
68
+ )}
69
+ >
70
+
71
+ </span>
72
+ <span
73
+ className={clsx(
74
+ 'text-xs text-center text-[#363636] w-[50px] lg:w-[110px]',
75
+ activeTab === 'all' && 'font-bold'
76
+ )}
77
+ >
78
+ All Quotes (<span>{quotations?.length}</span>)
79
+ </span>
80
+ </div>
81
+
82
+ <div
83
+ className="flex flex-col items-center space-y-1.5 transition-all cursor-pointer"
84
+ onClick={() => setActiveTab('pending')}
85
+ >
86
+ <span
87
+ className={twMerge(
88
+ clsx(
89
+ 'flex items-center text-xl justify-center w-9 h-9 rounded-full p-1.5 border border-[#eeeded] text-black bg-white'
90
+ ),
91
+ activeTab === 'pending' && 'bg-[#71d200] text-white'
92
+ )}
93
+ >
94
+
95
+ </span>
96
+ <span
97
+ className={clsx(
98
+ 'text-xs text-center text-[#363636] w-[50px] lg:w-[110px]',
99
+ activeTab === 'pending' && 'font-bold'
100
+ )}
101
+ >
102
+ Pending (<span>{tabCounts.pendingCount}</span>)
103
+ </span>
104
+ </div>
105
+
106
+ <div
107
+ className="flex flex-col items-center space-y-1.5 transition-all cursor-pointer"
108
+ onClick={() => setActiveTab('rejected')}
109
+ >
110
+ <span
111
+ className={twMerge(
112
+ clsx(
113
+ 'flex items-center text-xl justify-center w-9 h-9 rounded-full p-1.5 border border-[#eeeded] text-black bg-white'
114
+ ),
115
+ activeTab === 'rejected' && 'bg-[#71d200] text-white'
116
+ )}
117
+ >
118
+
119
+ </span>
120
+ <span
121
+ className={clsx(
122
+ 'text-xs text-center text-[#363636] w-[50px] lg:w-[110px]',
123
+ activeTab === 'rejected' && 'font-bold'
124
+ )}
125
+ >
126
+ Rejected (<span>{tabCounts.rejectedCount}</span>)
127
+ </span>
128
+ </div>
129
+
130
+ <div
131
+ className="flex flex-col items-center space-y-1.5 transition-all cursor-pointer"
132
+ onClick={() => setActiveTab('approved')}
133
+ >
134
+ <span
135
+ className={twMerge(
136
+ clsx(
137
+ 'flex items-center text-xl justify-center w-9 h-9 rounded-full p-1.5 border border-[#eeeded] text-black bg-white'
138
+ ),
139
+ activeTab === 'approved' && 'bg-[#71d200] text-white'
140
+ )}
141
+ >
142
+
143
+ </span>
144
+ <span
145
+ className={clsx(
146
+ 'text-xs text-center text-[#363636] w-[50px] lg:w-[110px]',
147
+ activeTab === 'approved' && 'font-bold'
148
+ )}
149
+ >
150
+ Approved (<span>{tabCounts.approvedCount}</span>)
151
+ </span>
152
+ </div>
153
+
154
+ <div
155
+ className="flex flex-col items-center space-y-1.5 transition-all cursor-pointer"
156
+ onClick={() => setActiveTab('sending_approval_request')}
157
+ >
158
+ <span
159
+ className={twMerge(
160
+ clsx(
161
+ 'flex items-center text-xl justify-center w-9 h-9 rounded-full p-1.5 border border-[#eeeded] text-black bg-white'
162
+ ),
163
+ activeTab === 'sending_approval_request' &&
164
+ 'bg-[#71d200] text-white'
165
+ )}
166
+ >
167
+
168
+ </span>
169
+ <span
170
+ className={clsx(
171
+ 'text-xs text-center text-[#363636] w-[50px] lg:w-[110px]',
172
+ activeTab === 'sending_approval_request' && 'font-bold'
173
+ )}
174
+ >
175
+ Requires My Approval (
176
+ <span>{tabCounts.sendingApprovalRequestCount}</span>)
177
+ </span>
178
+ </div>
179
+
180
+ <span className="absolute h-px bg-[#eeeded] top-[18px] left-6 -translate-y-1/2 lg:left-1/2 lg:-translate-x-1/2 z-[-1] w-[250px] lg:w-[calc(100%-110px)]"></span>
181
+ </div>
182
+ </div>
183
+ );
184
+ }