1 пункт сделан
2 нужно доработать
This commit is contained in:
gofnnp 2022-11-15 07:38:37 +04:00
parent 5d5a04c00f
commit 3120528bb2
19 changed files with 283 additions and 130 deletions

View File

@ -1,12 +1,12 @@
import {MainPageCode, OrderStatus, Page, PageCode, PaymentMethod} from "./interface/data";
export const PageList: Page[] = [
{
code: PageCode.Auth,
name: 'Вход',
resName: 'auth',
onSideBar: false,
},
// {
// code: PageCode.Auth,
// name: 'Вход',
// resName: 'auth',
// onSideBar: false,
// },
{
code: PageCode.Orders,
name: 'Заказы',
@ -16,12 +16,12 @@ export const PageList: Page[] = [
];
export const PageListWithBonus: Page[] = [
{
code: PageCode.Auth,
name: 'Вход',
resName: 'auth',
onSideBar: false,
},
// {
// code: PageCode.Auth,
// name: 'Вход',
// resName: 'auth',
// onSideBar: false,
// },
{
code: PageCode.BonusProgram,
name: 'Ваша карта лояльности',

View File

@ -42,6 +42,7 @@ import { TreeSelectModule } from 'primeng/treeselect';
import { UserDataOrderComponent } from './components/user-data-order/user-data-order.component';
import {DropdownModule} from "primeng/dropdown";
import {SelectButtonModule} from 'primeng/selectbutton';
import { CalendarModule } from 'primeng/calendar';
@NgModule({
declarations: [
@ -99,7 +100,8 @@ import {SelectButtonModule} from 'primeng/selectbutton';
ListboxModule,
TreeSelectModule,
DropdownModule,
SelectButtonModule
SelectButtonModule,
CalendarModule
],
providers: [DialogService, MessageService, MessagingService ],
bootstrap: [AppComponent]

View File

@ -1,18 +1,20 @@
<div *ngFor="let option of options" class="extra_options_checkbox">
<div *ngFor="let option of options; let index = index;" class="extra_options_checkbox">
<div class="extra_options_label"><label>{{option.name}}</label></div>
<div class="extra_options_value">
<div class="woofood-cbx-wrapper">
<input class="inp-woofood-cbx"
[disabled]="!optionIsSelected(option) && selectedOptions.length >= modifier.restrictions.maxQuantity"
[checked]="optionIsSelected(option)" type="checkbox" style="display: none" name="add_extra_option"
value="{{option.id}}" id="{{option.id}}" (change)="onToggle(option)">
[disabled]="!optionIsSelected(option) && selectedOptions.length >= modifier.restrictions.maxQuantity && (modifier.restrictions.maxQuantity > 1 || !modifier.restrictions.minQuantity)"
[checked]="optionIsSelected(option)"
[type]="modifier.restrictions.maxQuantity > 1 || !modifier.restrictions.minQuantity ? 'checkbox' : 'radio'"
style="display: none" name="add_extra_option" value="{{option.id}}" id="{{option.id}}"
(change)="onToggle(option)">
<label class="woofood-cbx" for="{{option.id}}">
<span>
<svg width="12px" height="10px" viewBox="0 0 12 10">
<polyline points="1.5 6 4.5 9 10.5 1"></polyline>
</svg>
</span>
<span>{{currencySymbol}}{{0}}</span>
<span>{{currencySymbol}}{{option.price}}</span>
</label>
</div>
</div>

View File

@ -27,7 +27,7 @@ export class ProductModalComponent implements OnInit {
ngOnInit(): void {
this.product = this.config.data.product
this.modifiersGroups = this.config.data.modifiersGroups
this.modifiers = this.config.data.modifiers
this.modifiers = this.config.data.modifiers
this.cartProduct = new CartProduct(this.product.id, this.product.name, this.modifiersFilter(), this.modifiers);
}
@ -39,20 +39,30 @@ export class ProductModalComponent implements OnInit {
return this.modifiers.filter((modifier) => modifier.groupId === modifierGroup.id)
}
selectedOptions(modifier: ModifiersGroup): Modifier[]{
return this.cartProduct.modifiers.find(cartModifier => cartModifier.id === modifier.id)?.options ?? [];
selectedOptions(modifier: ModifiersGroup): Modifier[] {
const cartModifier = this.cartProduct.modifiers.find(cartModifier => cartModifier.id === modifier.id)
if (modifier.restrictions.maxQuantity === 1 && modifier.restrictions.minQuantity === 1) {
cartModifier?.options.push(this.optionsFilter(modifier)[0])
}
return cartModifier?.options ?? [];
}
addOption(modifier: ModifiersGroup, option: Modifier){
addOption(modifier: ModifiersGroup, option: Modifier) {
const modif = this.cartProduct.modifiers.find((modif) => modif.id === modifier.id);
const optionSelectedCartIndex = modif?.options.findIndex((modif) => modif.id === option.id)
if ((optionSelectedCartIndex || optionSelectedCartIndex === 0) && optionSelectedCartIndex !== -1) {
modif?.options.splice(optionSelectedCartIndex, 1)
return
const optionSelectedCartIndex = modif?.options.findIndex((modif) => modif.id === option.id)
if (modifier.restrictions.maxQuantity === 1 && modifier.restrictions.minQuantity === 1) {
if (modif?.options) {
modif.options.length = 0
}
} else {
if ((optionSelectedCartIndex || optionSelectedCartIndex === 0) && optionSelectedCartIndex !== -1) {
modif?.options.splice(optionSelectedCartIndex, 1)
return
}
}
modif?.options.push(option)
}
getIsShow(element: HTMLDivElement) {
const isShow = Object.values(element.attributes).find((value) => value.name === 'isshow')?.value
return isShow === 'true'
@ -67,7 +77,7 @@ export class ProductModalComponent implements OnInit {
element.setAttribute('isShow', 'false')
return
}
element.setAttribute('isShow', 'true')
element.setAttribute('isShow', 'true')
}
addToCart(event: Event) {

View File

@ -8,28 +8,55 @@
<p сlass="form-row form-row-wide">
<input formControlName="first_name" id="first_name" pInputText placeholder="Ваше имя" type="text">
</p>
<p *ngIf="deliverData.deliveryType?.title === 'Доставка'" сlass="form-row form-row-wide">
<input formControlName="street" id="street" pInputText placeholder="Улица" type="text">
</p>
<p *ngIf="deliverData.deliveryType?.title === 'Доставка'" сlass="form-row form-row-first">
<input formControlName="house" id="house" pInputText placeholder="Номер дома" type="text">
</p>
<p *ngIf="deliverData.deliveryType?.title === 'Доставка'" сlass="form-row form-row-last">
<input formControlName="flat" id="flat" pInputText placeholder="Квартира" type="number" min="1">
</p>
<div *ngIf="deliverData.deliveryType?.title === 'Доставка'">
<p сlass="form-row form-row-last">
<input formControlName="flat" id="flat" pInputText placeholder="Квартира" type="number" min="1">
</p>
<p сlass="form-row form-row-wide">
<input formControlName="street" id="street" pInputText placeholder="Улица" type="text">
</p>
<p сlass="form-row form-row-first">
<input formControlName="house" id="house" pInputText placeholder="Номер дома" type="text">
</p>
</div>
<label class="terminal-list-label">Пункты самовывоза</label>
<div *ngIf="deliverData.deliveryType?.title === 'Самовывоз'" class="terminal-list-container">
<div *ngFor="let terminal of terminalList" [ngClass]="{
'terminal-container': true,
'selected': terminal.label === selectedTerminal.label
}">
<img src="{{terminal.image}}" alt="{{terminal.address}}">
<span class="terminal-container__name">{{terminal.label}}</span>
<span class="terminal-container__address">{{terminal.address}}</span>
</div>
</div>
</div>
<div formGroupName="deliveryDataForm">
<p сlass="form-row form-row-wide">
<p-dropdown [options]="deliveryTypes" formControlName="deliveryType" placeholder="Доставка"
optionLabel="title" (onChange)="changeDeliveryType($event)"></p-dropdown>
optionLabel="title" (onChange)="changeDeliveryType($event)" [disabled]="true"></p-dropdown>
</p>
<p сlass="form-row form-row-wide">
<p-selectButton [options]="paymentMethods" formControlName="paymentMethod" optionLabel="label">
</p-selectButton>
<label id="deliveryDate">Время выдачи</label>
<p-calendar
formControlName="deliveryDate"
[showTime]="true"
[showSeconds]="false"
[disabled]="true"
[touchUI]="true"
inputId="time"
placeholder="Время выдачи"
dateFormat="dd.mm.yy"
></p-calendar>
</p>
<p сlass="form-row form-row-last">
<p сlass="form-row form-row-wide">
<!-- <p-selectButton [options]="paymentMethods" formControlName="paymentMethod" optionLabel="label">
</p-selectButton> -->
*Оплата бонусами
</p>
<!-- <p сlass="form-row form-row-last">
<input [maxLength]="255" id="promo-code" pInputText placeholder="Промокод" type="text">
</p>
</p> -->
<p сlass="form-row form-row-wide">
<textarea [maxLength]="255" cols="30" formControlName="comment" pInputTextarea placeholder="Комментарий"
rows="1"></textarea>

View File

@ -1,6 +1,7 @@
:host {
.woocommerce-shipping-fields__field-wrapper {
margin-top: 8px;
margin: 8px auto 0 auto;
max-width: 400px;
}
@ -64,4 +65,57 @@
}
}
.terminal-list-container {
display: flex;
flex-wrap: wrap;
gap: 16px;
margin: 8px 0 16px;
.terminal-container {
width: calc(50% - 8px);
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
border: solid #09467f 1px;
border-radius: 16px;
overflow: hidden;
padding-bottom: 16px;
opacity: .5;
text-align: center;
&.selected {
opacity: 1;
}
img {
width: 100%;
height: 120px;
object-fit: cover;
}
&__name {}
&__address {
font-size: 14px;
}
}
}
.terminal-list-label {
margin-left: 8px;
font-size: 14px;
}
.form-row-wide {
display: flex;
flex-direction: column;
}
#deliveryDate {
margin-left: 8px;
margin-bottom: 4px;
display: block;
font-size: 14px;
}
}

View File

@ -9,7 +9,9 @@ import { CartService } from 'src/app/services/cart.service';
import { environment } from "../../../environments/environment";
import { MessageService } from "primeng/api";
import { WpJsonService } from "../../services/wp-json.service";
import { HttpClientModule } from '@angular/common/http';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { CookiesService } from 'src/app/services/cookies.service';
import moment from 'moment';
@ -27,7 +29,6 @@ export class UserDataOrderComponent implements OnInit {
public hasError = false;
public mainFormGroup!: FormGroup;
public deliveryTypes: DeliveryType[] = [];
public minDate!: Date;
public new_street!: string | null;
public street!: string;
public new_house!: string | null;
@ -44,12 +45,14 @@ export class UserDataOrderComponent implements OnInit {
phone: null,
};
public deliverData: DeliveryData = {
deliveryDate: null,
deliveryDate: moment().add(1, 'hours').toDate(),
deliveryType: null,
paymentMethod: paymentMethods[0],
comment: '',
persons: 1,
persons: 1
};
public terminalList!: any;
public selectedTerminal!: any;
constructor(
private fb: FormBuilder,
@ -59,12 +62,28 @@ export class UserDataOrderComponent implements OnInit {
private cartService: CartService,
private messageService: MessageService,
private wpJsonService: WpJsonService,
private http: HttpClient,
private cookiesService: CookiesService,
) {
}
ngOnInit(): void {
this.minDate = new Date();
ngOnInit(): void {
this._createMainForm();
this.getTerminalList();
this.selectedTerminal = JSON.parse(this.cookiesService.getItem('selectedTerminal') || '')
}
getTerminalList() {
this.http.get('./assets/terminal_list1.json').subscribe({
next: (value) => {
this.terminalList = value
},
error: (err) => {
console.error(err);
}
})
}
changeDeliveryType(event: any) {
@ -187,13 +206,13 @@ export class UserDataOrderComponent implements OnInit {
"type": "self_delivery"
}
];
this.deliverData.deliveryType = this.deliveryTypes[0];
this.deliverData.deliveryType = this.deliveryTypes[1];
return this.fb.group({
// deliveryDate: [this.deliverData.deliveryDate, []],
deliveryDate: [this.deliverData.deliveryDate, []],
deliveryType: [this.deliverData.deliveryType, [Validators.required]],
paymentMethod: [this.deliverData.paymentMethod, [Validators.required]],
// persons: [this.deliverData.persons, [Validators.required, Validators.minLength(2), Validators.maxLength(255),]],
comment: [this.deliverData.comment, [Validators.maxLength(255),]],
comment: [this.deliverData.comment, [Validators.maxLength(255),]]
});
}
}

View File

@ -2,9 +2,9 @@ import { CartProduct } from "../models/cart-product";
export enum PageCode {
Auth,
Orders,
// Auth,
BonusProgram,
Orders,
RefSystem,
UserData
}

View File

@ -1,21 +1,17 @@
<div [ngClass]="{
woocommerce: true,
'auth-page': currentPage.code === PageCode.Auth
'auth-page': showAuthoriztion
}">
<nav *ngIf="currentPage.code !== PageCode.Auth" class="main-menu-container">
<nav class="main-menu-container">
<ul>
<ng-container *ngFor="let page of mainPageList; let index = index; let last = last; let first = first;">
<li *ngIf="page.onSideBar" class="main-menu-container__item"
[ngClass]="{
<li *ngIf="page.onSideBar" class="main-menu-container__item" [ngClass]="{
'cart': page.resName === 'cart',
'is-active': page === currentPageMain
}"
[ngStyle]="{
}" [ngStyle]="{
border: last && 0,
'border-radius': first ? '6px 0 0 6px' : (last ? '0 6px 6px 0' : 0)
}"
[attr.data-counter]="page.resName === 'cart' ? cartCount : null"
(click)="changeMainPage(page, $event)">
}" [attr.data-counter]="page.resName === 'cart' ? cartCount : null" (click)="changeMainPage(page, $event)">
<span>
{{page.name}}
</span>
@ -25,12 +21,9 @@
</nav>
<div [ngSwitch]="currentPageMain.code">
<ng-container *ngSwitchCase="MainPageCode.Account">
<div class="account-page">
<div *ngIf="currentPage.code !== PageCode.Auth" class="top-left-attribute"></div>
<div *ngIf="!showAuthoriztion" class="account-page">
<div *ngIf="showAuthoriztion" class="top-left-attribute"></div>
<div [ngSwitch]="currentPage.code" class="">
<ng-container *ngSwitchCase="PageCode.Auth">
<app-auth [handleHttpError]="handleHttpErrorFunc" (phoneConfirmed)="phoneConfirmed()"></app-auth>
</ng-container>
<ng-container *ngSwitchCase="PageCode.Orders">
<app-orders (deauthorization)="changePage(pageList[0])"></app-orders>
</ng-container>
@ -44,7 +37,7 @@
<app-ref-system></app-ref-system>
</ng-container>
</div>
<nav *ngIf="currentPage.code !== PageCode.Auth" class="woocommerce-MyAccount-navigation">
<nav *ngIf="!showAuthoriztion" class="woocommerce-MyAccount-navigation">
<ul>
<ng-container *ngFor="let page of pageList; let index = index">
<li *ngIf="page.onSideBar" class="woocommerce-MyAccount-navigation-link" [ngClass]="{
@ -76,7 +69,12 @@
<app-products></app-products>
</ng-container>
<ng-container *ngSwitchCase="MainPageCode.Cart">
<app-cart></app-cart>
<app-cart *ngIf="!showAuthoriztion" (showAuthoriztion)="checkAuthorization($event)"></app-cart>
</ng-container>
<ng-container>
<app-auth *ngIf="showAuthoriztion && currentPageMain !== mainPageList[1]" [handleHttpError]="handleHttpErrorFunc"
(phoneConfirmed)="phoneConfirmed()"></app-auth>
</ng-container>
</div>

View File

@ -5,9 +5,6 @@
position: relative;
&.auth-page {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.main-menu-container {

View File

@ -44,45 +44,45 @@ export class AccountComponent implements OnInit {
readonly MainPageCode = MainPageCode;
readonly mainPageList = PageListMain;
public currentPageMain: Page = this.mainPageList[environment.production ? 0 : 1];
public currentPageMain: Page = this.mainPageList[environment.production ? 1 : 1];
public cartCount = 0;
public showAuthoriztion = false;
ngOnInit(): void {
if (!this.getToken()) {
this.currentPage = this.pageList[0];
} else {
this.route.queryParams.subscribe((params) => {
if (!params['activePage']) {
this.currentPage = this.pageList[1];
return;
}
const currentPage = this.pageList.find((page) => {
return page.code === Number(params['activePage']);
});
if (!currentPage) {
this.currentPage = this.pageList[1];
} else {
this.currentPage = currentPage;
}
});
this.cartCount = this.cartService.cartCount;
this.cartService.cartCount$.subscribe({
next: (count) => {
this.cartCount = count;
document.querySelectorAll('.cart')[0].setAttribute("data-counter", this.cartCount.toString())
}
});
}
this.checkAuthorization(true)
this.currentPage = this.pageList[0];
document.body.classList.add(
'woocommerce-account',
'woocommerce-page',
'woocommerce-orders'
);
this.route.queryParams.subscribe((params) => {
if (!params['activeAccountPage']) {
this.currentPage = this.pageList[0];
return;
}
const currentPage = this.pageList.find((page) => {
return page.code === Number(params['activeAccountPage']);
});
if (!currentPage) {
this.currentPage = this.pageList[0];
} else {
this.currentPage = currentPage;
}
});
this.cartCount = this.cartService.cartCount;
this.cartService.cartCount$.subscribe({
next: (count) => {
this.cartCount = count;
document.querySelectorAll('.cart')[0].setAttribute("data-counter", this.cartCount.toString())
}
});
}
phoneConfirmed(): void {
this.refSystem();
this.currentPage = this.pageList[1];
this.checkAuthorization(false, true)
}
async refSystem() {
@ -156,6 +156,10 @@ export class AccountComponent implements OnInit {
// },
queryParamsHandling: 'merge',
});
this.checkAuthorization(true)
if (this.currentPageMain === this.mainPageList[2]) {
this.checkAuthorization(false)
}
}
changePage(page: Page, event?: MouseEvent): void {
@ -163,20 +167,21 @@ export class AccountComponent implements OnInit {
event.preventDefault();
}
this.currentPage = page;
// let params = new HttpParams();
// params = params.append('activePage', this.currentPage.code);
this.router.navigate(['.'], {
relativeTo: this.route,
queryParams: {
activePage: this.currentPage.code,
activeAccountPage: this.currentPage.code,
},
queryParamsHandling: 'merge',
// preserve the existing query params in the route
// skipLocationChange: true
// do not trigger navigation
});
}
checkAuthorization(showAuthoriztion: boolean, forced = false) {
if (!this.getToken() || forced) {
this.showAuthoriztion = showAuthoriztion
}
}
handleHttpError(error: HttpErrorResponse): void {
if (error.status === 500) {
this.logout();
@ -214,6 +219,7 @@ export class AccountComponent implements OnInit {
'max-width': '90vw',
overflow: 'auto',
'border-radius': '4px',
padding: '16px'
},
baseZIndex: 10000,
autoZIndex: true,
@ -224,7 +230,7 @@ export class AccountComponent implements OnInit {
this.ref.onClose.subscribe((result) => {
if (result) {
this.deleteToken();
this.currentPage = this.pageList[0];
this.showAuthoriztion = true;
}
});
}

View File

@ -93,7 +93,7 @@
<app-user-data-order *ngIf="orderConfirmed" (orderSubmitted)="orderSubmitted()"></app-user-data-order>
<div #loadingEl *ngIf="loading">
<div #loadingEl *ngIf="loading && !orderConfirmed">
<div class="angular-spinner-container" style="width: fit-content; height: 100%; margin: 16px auto;">
<p-progressSpinner styleClass="angular-spinner"></p-progressSpinner>
</div>

View File

@ -1,4 +1,4 @@
import { Component, OnInit } from '@angular/core';
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { MessageService } from 'primeng/api';
import { Order } from 'src/app/models/order';
import { OrderProduct } from 'src/app/models/order-product';
@ -11,6 +11,7 @@ import { OrderService } from 'src/app/services/order.service';
styleUrls: ['./cart.component.scss']
})
export class CartComponent implements OnInit {
@Output() showAuthoriztion = new EventEmitter<boolean>();
public loading = false;
public orderConfirmed = false;
public order!: Order;
@ -40,6 +41,7 @@ export class CartComponent implements OnInit {
confirmOrder(event: Event): void{
event.preventDefault();
this.showAuthoriztion.emit(true);
this.orderConfirmed = true;
// this.confirm.emit();
}

View File

@ -1,5 +1,5 @@
<p-toast position="top-center"></p-toast>
<div class="products-container">
<div *ngIf="!loading; else loadingEl" class="products-container">
<div class="products-container__filters-container">
<label>Категория</label>
<p-treeSelect *ngIf="groups" [(ngModel)]="selectedGroup" [options]="groups"
@ -41,4 +41,9 @@
</div>
</ng-template>
</p-toast>
</div>
</div>
<ng-template #loadingEl>
<div class="angular-spinner-container" style="width: fit-content; height: 100%; margin: 16px auto;">
<p-progressSpinner styleClass="angular-spinner"></p-progressSpinner>
</div>
</ng-template>

View File

@ -71,17 +71,4 @@
bottom: 0;
}
}
}
// :host ::ng-deep {
// .p-toast-message-custom {
// background-color: #E1CFE7;
// border: solid #8A427A;
// border-width: 0 0 0 6px;
// color: #2c1e30;
// .p-toast-icon-close {
// color: #2c1e30;
// }
// }
// }
}

View File

@ -23,6 +23,7 @@ export class ProductsComponent implements OnInit {
public selectedGroup!: Group;
public terminalList!: any;
public selectedTerminal!: any;
public loading: boolean = false;
constructor(
public dialogService: DialogService,
@ -33,6 +34,7 @@ export class ProductsComponent implements OnInit {
) { }
async ngOnInit() {
this.loading = true;
await this.getTerminalList()
this.getData()
this.messageService.add({
@ -43,6 +45,7 @@ export class ProductsComponent implements OnInit {
setTimeout(() => {
this.confirmTerminalList()
}, 0)
this.loading = false;
}
async getTerminalList() {
@ -134,8 +137,11 @@ export class ProductsComponent implements OnInit {
changeTerminal() {
setTimeout(() => {
this.products.length = 0;
this.loading = true;
this.getData()
this.cartService.changeTerminal(this.selectedTerminal)
this.cartService.changeTerminal(this.selectedTerminal);
this.loading = false;
}, 0);
}

View File

@ -0,0 +1,14 @@
[
{
"label": "Не основная группа",
"id": "5613101b-e9bf-3e26-0175-88c7ab7e00cf",
"image": "https://idei.club/uploads/posts/2022-03/1647392267_5-idei-club-p-supermarket-interer-interer-krasivo-foto-6.jpg",
"address": "ул. Ленина 16"
},
{
"label": "Основная группа",
"id": "5613101b-e9bf-3e26-0175-88c7ab7e00cf",
"image": "https://idei.club/uploads/posts/2022-03/1647392267_5-idei-club-p-supermarket-interer-interer-krasivo-foto-6.jpg",
"address": "ул. Ленина 18"
}
]

View File

@ -7,7 +7,7 @@ export const environment = {
appWPEndpoint: './',
hasBonusProgram: true,
systemId: 'StyrkNFW9vKga1KlJP',
defaultUrl: 'http://192.168.0.179:4200',
defaultUrl: 'http://192.168.0.12:4200',
firebase: {
apiKey: 'AIzaSyCnKvln5itnrBj62POCPHxshAN_Vmd0zds',
authDomain: 'fashionlogicanotification.firebaseapp.com',

View File

@ -143,9 +143,33 @@ input::-webkit-date-and-time-value {
}
}
@media screen and (max-width: 960px) {
.grid.p-fluid button {
.grid.p-fluid button {
width: 100%;
margin-bottom: .5rem;
}
p-toast[key="c"] .p-toast {
width: 96vw;
max-width: 400px;
}
.p-calendar {
width: 100%;
.p-inputtext {
width: 100%;
margin-bottom: .5rem;
color: #000000;
border: 1px solid #000000;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", Segoe UI Symbol;
font-size: 1rem;
color: #495057;
background: #ffffff;
padding: 0.5rem 0.75rem;
border: 1px solid #ced4da;
transition: background-color 0.15s, border-color 0.15s, box-shadow 0.15s;
-webkit-appearance: none;
appearance: none;
border-radius: 4px;
cursor: pointer;
}
}