diff --git a/.gitignore b/.gitignore index 4a7f73a2..613976e8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ logs .env .env.* !.env.example + +# AI +.claude \ No newline at end of file diff --git a/assets/css/fonts.css b/assets/css/fonts.css index 775a595b..145d5ef1 100644 --- a/assets/css/fonts.css +++ b/assets/css/fonts.css @@ -8,13 +8,20 @@ @font-face { font-family: 'Plumb'; src: url('/fonts/plumb-bo-webfont.woff2') format('woff2'); - font-weight: bold; + font-weight: 700; + font-style: normal; +} + +@font-face { + font-family: 'Plumb'; + src: url('/fonts/plumb-me-webfont.woff2') format('woff2'); + font-weight: 500; font-style: normal; } @font-face { font-family: 'Plumb'; src: url('/fonts/plumb-re-webfont.woff2') format('woff2'); - font-weight: normal; + font-weight: 400; font-style: normal; } diff --git a/assets/icons/spinner.svg b/assets/icons/spinner.svg new file mode 100644 index 00000000..8a27e526 --- /dev/null +++ b/assets/icons/spinner.svg @@ -0,0 +1,3 @@ + diff --git a/common/constants.ts b/common/constants.ts index 8c93e13c..77843813 100644 --- a/common/constants.ts +++ b/common/constants.ts @@ -1,26 +1,25 @@ export enum ArticleType { - WithoutSubCategories, - WithSubCategories, - SubCategory, + WithoutSubCategories, + WithSubCategories, + SubCategory, } export enum FilterTypeKeys { - ByCategory = '0', - ByDate = '1', + ByCategory = '0', + ByDate = '1', } export const FilterTypeParams = { - [FilterTypeKeys.ByCategory]: 'filter_category', - [FilterTypeKeys.ByDate]: 'filter_date', -} + [FilterTypeKeys.ByCategory]: 'filter_category', + [FilterTypeKeys.ByDate]: 'filter_date', +}; export enum FilterDateKeys { - ASC = '0', - DESC = '1', + ASC = '0', + DESC = '1', } export const FilterDateParams = { - [FilterDateKeys.ASC]: 'filter_asc', - [FilterDateKeys.DESC]: 'filter_desc', -} - + [FilterDateKeys.ASC]: 'filter_asc', + [FilterDateKeys.DESC]: 'filter_desc', +}; diff --git a/common/types.ts b/common/types.ts index 5ebda94c..c0ab4d03 100644 --- a/common/types.ts +++ b/common/types.ts @@ -55,7 +55,7 @@ export interface Product { inv_final_assembly: number; inv_ready_to_assembly: number; inv_scheduled: number; - }, + }; options: ProductOption[]; components: ProductOption[]; use_cases: string[]; @@ -66,3 +66,107 @@ export type Video = [string, string, string, any?]; export type PhotoLink = [string, string, string?, string?]; export type LogoLink = [string, string, string]; + +export enum DeliveryType { + Courier = 'courier', + Pickup = 'pickup', + Point = 'point', + Taxi = 'todoor', +} + +export const RUSSIA_ID = 643; + +export interface AvailableDelivery { + id: string; + type: string; + daysMin: number | null; + daysMax: number | null; + title: string; + address: string | null; + mapUrl: string | null; + price: number; + total: number; + error?: string | null; +} + +export interface AvailableDeliveriesInfo{ + available: AvailableDelivery[]; + freeDelivery: boolean; +} + +export interface CustomerData{ + individual:{ + fio: string; + phone: string; + additional: string; + email: string; + comment: string; + } + entity: { + inn: string; + orgName: string; + address: string; + email: string; + comment: string; + }; + payerType: 'individual' | 'entity'; +} + +export interface RecentAddress { + city: string; + postcode: string; + address: string; +} + +export interface RecentOrg { + inn: string; + orgName: string; + address: string; +} + +export interface OrderInfo { + payerType: string; + payerData: CustomerData; + deliveryType: string; + deliveryData: Record; + paymentType: string; + basketData: Record; + countries: Record; + cdekCountries: number[]; + recentAddresses?: RecentAddress[]; + recentOrgs?: RecentOrg[]; +} + +export interface Tariff { + calendar_max: number; + calendar_min: number; + delivery_date_range: { min: string; max: string }; + delivery_mode: number; + delivery_sum: number; + period_max: number; + period_min: number; + tariff_code: number; + tariff_description: string; + tariff_name: string; +} + +export interface Destination { + city_code: number; + city: string; + type: string; + postal_code: string; + country_code: string; + region: string; + have_cashless: boolean; + have_cash: boolean; + allowed_cod: boolean; + is_dressing_room: boolean; + code: string; + name: string; + address: string; + work_time: string; + location: [number, number]; + weight_min: number; + weight_max: number; + dimensions: any; +} diff --git a/components/Button.vue b/components/Button.vue index 45d39584..f0e6499b 100644 --- a/components/Button.vue +++ b/components/Button.vue @@ -1,20 +1,36 @@ @@ -24,24 +40,45 @@ withDefaults(defineProps<{ id?: string; label: string; size?: 'default' | 'large border-radius: 4px; white-space: nowrap; position: relative; - min-height: 33px; + min-height: 31px; display: inline-flex; - cursor: pointer; user-select: none; align-items: center; justify-content: center; overflow: hidden; - background: var(--primary-color); - color: #fff; - border: 0; - transition: background 0.2s; + transition: background 0.3s, color 0.3s; font-weight: bold; outline: none; line-height: 24px; } -.wb-button:hover { - background: var(--primary-color-hover); +.wb-button:not(:disabled) { + cursor: pointer; +} + +.wb-button:disabled { + filter: opacity(0.5); +} + +.wb-button-primary { + background: var(--primary-color); + border: 1px solid var(--primary-color); + color: #fff; +} + +.wb-button-primary:hover { + background: var(--primary-color-hover) !important; + color: #fff !important; +} + +.wb-button-primary.wb-button-outlined { + background: transparent; + color: var(--primary-color) +} + +.wb-button-primary.wb-button-outlined { + background: transparent; + color: var(--primary-color) } .wb-button-m { @@ -53,4 +90,20 @@ withDefaults(defineProps<{ id?: string; label: string; size?: 'default' | 'large padding: 12px 24px; font-size: 18px; } + +.wb-button-spinner { + position: absolute; + width: 22px; + height: 22px; + animation: spin 1s linear infinite; +} + +.wb-button-primary .wb-button-spinner { + color: #fff; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} diff --git a/components/Input.vue b/components/Input.vue new file mode 100644 index 00000000..4191c25f --- /dev/null +++ b/components/Input.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/components/Loader.vue b/components/Loader.vue new file mode 100644 index 00000000..2df33a8e --- /dev/null +++ b/components/Loader.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/components/Select.vue b/components/Select.vue index 025006d1..31b67791 100644 --- a/components/Select.vue +++ b/components/Select.vue @@ -1,23 +1,40 @@ @@ -29,16 +46,31 @@ const model = defineModel(); position: relative; user-select: none; border: 1px solid var(--border-color); - border-radius: 6px; + border-radius: 2px; outline-color: transparent; box-shadow: var(--input-shadow); font-size: 16px; font-family: 'Plumb', sans-serif; - transition: border-color 0.2s; + transition: border-color 0.2s, outline-offset 0.2s; + +} + +[data-pc-name="select"]:has([role="combobox"]:focus) { + outline: 2px solid var(--link-color); + outline-offset: 3px; +} + +[data-pc-section="root"]:has([aria-disabled="true"]) { + background: var(--gray-color); +} + +[aria-disabled="true"] + [data-pc-section="dropdown"] { + display: none; } -[data-pc-name="select"]:hover { - border: 1px solid var(--border-color-hover); +[data-pc-name="select"]:focus { + outline: 2px solid var(--link-color); + outline-offset: 3px; } [data-pc-section="label"] { @@ -47,7 +79,6 @@ const model = defineModel(); overflow: hidden; flex: 1 1 auto; width: 1%; - padding: 7px 12px 7px 12px; text-overflow: ellipsis; cursor: pointer; border: 0 none; @@ -61,6 +92,7 @@ const model = defineModel(); flex-shrink: 0; margin-right: 12px; background: transparent; + z-index: 1000; } [data-pc-section="clearicon"] { @@ -69,13 +101,17 @@ const model = defineModel(); [data-pc-section="listcontainer"] { margin-top: 3px; + overflow: auto; + display: block; + z-index: 1000; } -[role="listbox"] { +[data-pc-section="overlay"] { box-shadow: 1px 1px 12px var(--gray-color); border: 1px solid #e2e8f0; background: #fff; - border-radius: 6px; + overflow: hidden; + border-radius: 2px; } [data-pc-section="list"] { @@ -87,7 +123,7 @@ const model = defineModel(); } [data-pc-section="option"] { - border-radius: 6px; + border-radius: 2px; padding: 6px 10px; cursor: pointer; margin-bottom: 2px; @@ -105,4 +141,51 @@ const model = defineModel(); background: var(--primary-color); color: #fff; } + +[data-pc-section="header"][data-pc-section="root"] { + position: relative; + display: block; +} + +[data-pc-name="pcfilter"] { + width: calc(100% - 12px); + padding-inline-end: 20px; + margin: 6px; + height: 30px; + border: 1px solid var(--border-color); + border-radius: 4px; +} + +[data-pc-name="inputicon"] { + position: absolute; + top: 2px; + right: 12px; + margin-top: 12px; + color: darkgray; + line-height: 1; + z-index: 1; +} + +[data-pc-section="label"] { + color: #000; + font-size: 16px; + //font-weight: 500; +} + +[data-pc-name="iconfield"] { + background: #fff; +} + +.wb-select-default { + padding: 26px 12px 18px; +} + +.wb-select-default [data-pc-section="label"] { + font-size: 24px; + font-weight: 500; +} + +.wb-select-small { + padding: 7px 12px 7px 12px; +} diff --git a/components/Textarea.vue b/components/Textarea.vue new file mode 100644 index 00000000..babc5b77 --- /dev/null +++ b/components/Textarea.vue @@ -0,0 +1,72 @@ + + +