@hhgtech/hhg-components
Version:
Hello Health Group common components
1,058 lines (1,007 loc) • 89.8 kB
JavaScript
'use strict';
var React = require('react');
var core$1 = require('@hhgtech/icons/core');
var core = require('@mantine/core');
var hooks = require('@mantine/hooks');
var utils$1 = require('./utils-bd6d1948.js');
var useOutsideClick = require('./useOutsideClick-8e631203.js');
var index$1 = require('./index-2b476eb9.js');
var debounce = require('lodash/debounce');
var constants = require('./constants-961f54ac.js');
var styled = require('@emotion/styled');
var miscTheme = require('./miscTheme.js');
var utils = require('./utils-5e3a8440.js');
var index = require('./index-92dad635.js');
var index$2 = require('./index-d4ad3f79.js');
var ChevronDown = require('./ChevronDown-df80ec34.js');
var tslib_es6 = require('./tslib.es6-af09a0ba.js');
var arrow = require('@hhgtech/icons/arrow');
var index$3 = require('./index-47ec78f0.js');
var paths = require('./paths-a2a44eda.js');
var index$4 = require('./index-515469d0.js');
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
var React__default = /*#__PURE__*/_interopDefault(React);
var debounce__default = /*#__PURE__*/_interopDefault(debounce);
var styled__default = /*#__PURE__*/_interopDefault(styled);
const StyledSearchBarWrapper = styled__default["default"].div ``;
const StyleWrapperContainer = styled__default["default"].div `
background-color: ${miscTheme.theme.colors.neutral50};
padding-bottom: 1px;
${utils.MediaQueries.mbDown} {
padding: 0;
}
`;
const StyleSearchInputContainer = styled__default["default"](index.Container) `
padding: 0 16px;
${utils.MediaQueries.mbDown} {
padding: 0;
}
`;
const StyleSearchContent = styled__default["default"].div `
padding: 24px 56px;
border-radius: 16px;
background-color: ${miscTheme.theme.colors.white};
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.04),
0px 0px 2px 0px rgba(0, 0, 0, 0.06), 0px 4px 8px 0px rgba(0, 0, 0, 0.04);
${utils.MediaQueries.mbDown} {
padding: 16px;
border-radius: 0;
}
.flex-search-input {
flex-direction: row;
${utils.MediaQueries.mbDown} {
flex-direction: column;
}
}
`;
const SEARCH_KEYWORD_MIN_LENGTH = {
'en-PH': 3,
'tl-PH': 2,
'id-ID': 3,
'vi-VN': 2,
'zh-TW': 0,
'ms-MY': 2,
'th-TH': 2,
};
const initialState = {
isMobile: false,
isDebug: false,
navigateTo: null,
geoLocationPermission: 'unset',
geoLocationCoordinates: null,
focused: 'none',
focusedSpecialty: 'none',
searchLocationKeywords: '',
searchSpecialtyKeywords: '',
searchKeywords: '',
selectedLocation: null,
selectedSpecialty: null,
defaultCity: null,
initialCities: [],
cities: [],
searchResults: {},
initialSearchResults: {},
isLoadingCities: false,
isLoadingSearchResults: false,
isMobileModalOpened: false,
initialSelectedCity: null,
autoCompleteDisabled: false,
inputSearchPlaceholder: '',
locale: 'vi-VN',
allSpecialties: [],
popularKeys: [],
};
var ActionTypes;
(function (ActionTypes) {
ActionTypes["NavigateTo"] = "navigate to url";
ActionTypes["ClickOutsideInputLocation"] = "click outside input location";
ActionTypes["ClickOutsideInputSearch"] = "click outside input search";
ActionTypes["ClickOutsideInputSearchSpecialty"] = "click outside input search specialty";
ActionTypes["ClickOutsideInputSpecialty"] = "click outside input specialty";
ActionTypes["SelectCurrentLocation"] = "select current location";
ActionTypes["FocusInputLocation"] = "focus input location";
ActionTypes["FocusInputSearch"] = "focus input search";
ActionTypes["FocusInputSearchSpecialty"] = "focus input search specialty";
ActionTypes["ChangeInputLocation"] = "change input location";
ActionTypes["ChangeInputSearch"] = "change input search";
ActionTypes["ClearInputLocation"] = "clear input location";
ActionTypes["ClearInputSearch"] = "clear input search";
ActionTypes["SelectLocation"] = "select location";
ActionTypes["SelectSpecialty"] = "select specialty";
ActionTypes["FetchCitiesStart"] = "fetch cities start";
ActionTypes["FetchCitiesFinish"] = "fetch cities finish";
ActionTypes["FetchSearchResultsStart"] = "fetch search results start";
ActionTypes["FetchSearchResultsComplete"] = "fetch search results complete";
ActionTypes["FetchSearchResultsFinish"] = "fetch search results finish";
ActionTypes["FetchCitiesByGeoIpComplete"] = "fetch cities by geo IP finish";
ActionTypes["FetchCitiesByKeywordComplete"] = "fetch cities by keyword finish";
ActionTypes["DisplayInitialCities"] = "display initial cities";
ActionTypes["DisplayInitialSearchResults"] = "display initial search results";
ActionTypes["UpdateInitialSearchResults"] = "update initial search results";
ActionTypes["GrantGeoLocationPermission"] = "grant geolocation permission";
ActionTypes["DenyGeoLocationPermission"] = "deny geolocation permission";
ActionTypes["OpenMobileModal"] = "open mobile modal";
ActionTypes["CloseMobileModal"] = "close mobile modal";
ActionTypes["UpdateInitialSearchValue"] = "update initial search value";
ActionTypes["SelectSearchResult"] = "select search result";
ActionTypes["SetInputSearchPlaceholder"] = "set input search placeholder";
ActionTypes["FocusInputSpecialty"] = "focus input specialty";
ActionTypes["FetchAllSpecialties"] = "fetch all specialty";
ActionTypes["FetchPopularKeys"] = "fetch popular key by type";
})(ActionTypes || (ActionTypes = {}));
function resetSearchLocationKeywords(state) {
const { selectedLocation, searchLocationKeywords } = state;
if (selectedLocation && searchLocationKeywords !== selectedLocation.name) {
return Object.assign(Object.assign({}, state), { searchLocationKeywords: selectedLocation.name });
}
return state;
}
function reducer(state, action) {
var _a, _b;
if (state.isDebug) {
console.log('bookingSearchBarV3', { action, state });
}
switch (action.type) {
case ActionTypes.NavigateTo:
return Object.assign(Object.assign({}, state), { navigateTo: action.payload, focused: 'none', isMobileModalOpened: false });
case ActionTypes.ClickOutsideInputLocation: {
if (state.focused !== 'location') {
return state;
}
return resetSearchLocationKeywords(Object.assign(Object.assign({}, state), { focused: 'none' }));
}
case ActionTypes.ClickOutsideInputSpecialty: {
if (state.focused !== 'specialty') {
return state;
}
return resetSearchLocationKeywords(Object.assign(Object.assign({}, state), { focused: 'none' }));
}
case ActionTypes.ClickOutsideInputSearch: {
if (state.focused !== 'search') {
return state;
}
return Object.assign(Object.assign({}, state), { focused: 'none' });
}
case ActionTypes.ClickOutsideInputSearchSpecialty: {
if (state.focusedSpecialty !== 'search') {
return state;
}
return Object.assign(Object.assign({}, state), { focusedSpecialty: 'none' });
}
case ActionTypes.FocusInputLocation: {
const nextState = Object.assign(Object.assign({}, state), { focused: 'location' });
if (((_a = state.selectedLocation) === null || _a === void 0 ? void 0 : _a.id) === constants.ID_OPTION_CURRENT_LOCATION) {
nextState.searchLocationKeywords = '';
}
return nextState;
}
case ActionTypes.FocusInputSpecialty: {
const nextState = Object.assign(Object.assign({}, state), { focused: 'specialty' });
if (((_b = state.selectedLocation) === null || _b === void 0 ? void 0 : _b.id) === constants.ID_OPTION_CURRENT_LOCATION) {
nextState.searchLocationKeywords = '';
}
return nextState;
}
case ActionTypes.FocusInputSearch:
const nextState = Object.assign(Object.assign({}, state), { focused: 'search' });
if (state.isMobile) {
return resetSearchLocationKeywords(nextState);
}
return nextState;
case ActionTypes.FocusInputSearchSpecialty: {
const nextState = Object.assign(Object.assign({}, state), { focusedSpecialty: 'search' });
return nextState;
}
case ActionTypes.ChangeInputLocation:
return Object.assign(Object.assign({}, state), { searchLocationKeywords: action.payload });
case ActionTypes.ChangeInputSearch: {
const keyword = action.payload;
return Object.assign(Object.assign({}, state), { searchKeywords: keyword, isLoadingSearchResults: keyword.trim().length > SEARCH_KEYWORD_MIN_LENGTH[state.locale] });
}
case ActionTypes.SelectCurrentLocation:
return Object.assign(Object.assign({}, state), { focused: 'none', selectedLocation: action.payload, searchLocationKeywords: action.payload.name });
case ActionTypes.ClearInputLocation:
return Object.assign(Object.assign({}, state), { focused: 'location', cities: state.initialCities, searchLocationKeywords: '' });
case ActionTypes.ClearInputSearch:
return Object.assign(Object.assign({}, state), { focused: 'search', searchKeywords: '' });
case ActionTypes.SelectLocation:
return Object.assign(Object.assign({}, state), { focused: 'none', selectedLocation: action.payload, searchLocationKeywords: action.payload.name, searchResults: {} });
case ActionTypes.SelectSpecialty:
return Object.assign(Object.assign({}, state), { focused: 'none', selectedSpecialty: action.payload, searchResults: {} });
case ActionTypes.SelectSearchResult:
return Object.assign(Object.assign({}, state), { searchKeywords: action.payload.name, isMobileModalOpened: false, focused: 'none', navigateTo: action.payload.url });
case ActionTypes.FetchCitiesStart:
return Object.assign(Object.assign({}, state), { isLoadingCities: true });
case ActionTypes.FetchCitiesFinish:
return Object.assign(Object.assign({}, state), { isLoadingCities: false });
case ActionTypes.FetchSearchResultsStart:
return Object.assign(Object.assign({}, state), { isLoadingSearchResults: true });
case ActionTypes.FetchSearchResultsFinish:
return Object.assign(Object.assign({}, state), { isLoadingSearchResults: false });
case ActionTypes.FetchSearchResultsComplete: {
const nextState = Object.assign(Object.assign({}, state), { searchResults: Object.assign(Object.assign({}, state.searchResults), { [action.payload.key]: action.payload.searches }) });
return nextState;
}
case ActionTypes.FetchCitiesByGeoIpComplete: {
let { cities } = action.payload;
const { default: defaultCity } = action.payload;
cities = cities.filter((c) => c.id !== defaultCity.id);
if (defaultCity) {
cities.unshift(defaultCity);
}
if (state.initialSelectedCity &&
!cities.find((c) => c.id === state.initialSelectedCity.id)) {
cities.unshift(state.initialSelectedCity);
}
const nextState = Object.assign(Object.assign({}, state), { defaultCity, initialCities: cities, cities });
if (state.geoLocationPermission === 'denied' &&
!state.initialSelectedCity) {
nextState.selectedLocation = defaultCity;
nextState.searchLocationKeywords = defaultCity.name;
}
return nextState;
}
case ActionTypes.FetchCitiesByKeywordComplete:
return Object.assign(Object.assign({}, state), { cities: action.payload });
case ActionTypes.DisplayInitialCities:
return Object.assign(Object.assign({}, state), { cities: state.initialCities });
case ActionTypes.DisplayInitialSearchResults: {
const trimmed = state.searchKeywords.trim();
const minLen = SEARCH_KEYWORD_MIN_LENGTH[state.locale];
const shouldShowEmpty = trimmed.length > 0 && trimmed.length <= minLen;
return Object.assign(Object.assign({}, state), { searchResults: shouldShowEmpty ? {} : state.initialSearchResults, isLoadingSearchResults: false });
}
case ActionTypes.UpdateInitialSearchResults:
return Object.assign(Object.assign({}, state), { initialSearchResults: Object.assign(Object.assign({}, state.initialSearchResults), { [action.payload.key]: action.payload.searches }) });
case ActionTypes.GrantGeoLocationPermission:
return Object.assign(Object.assign({}, state), { geoLocationPermission: 'granted', geoLocationCoordinates: action.payload });
case ActionTypes.DenyGeoLocationPermission:
return Object.assign(Object.assign({}, state), { geoLocationPermission: 'denied', geoLocationCoordinates: null });
case ActionTypes.OpenMobileModal:
return Object.assign(Object.assign({}, state), { isMobileModalOpened: true });
case ActionTypes.CloseMobileModal:
return Object.assign(Object.assign({}, state), { isMobileModalOpened: false });
case ActionTypes.UpdateInitialSearchValue:
return Object.assign(Object.assign({}, state), { searchKeywords: action.payload });
case ActionTypes.SetInputSearchPlaceholder:
return Object.assign(Object.assign({}, state), { inputSearchPlaceholder: action.payload });
case ActionTypes.FetchAllSpecialties:
return Object.assign(Object.assign({}, state), { allSpecialties: action.payload });
case ActionTypes.FetchPopularKeys:
return Object.assign(Object.assign({}, state), { popularKeys: action.payload });
default:
return state;
}
}
const StyleSearchInputsWrapper = styled__default["default"].div `
flex: 1;
position: relative;
${utils.MediaQueries.mbDown} {
width: 100%;
}
`;
const StyleSearchBarContainer = styled__default["default"].div `
background: #f7f9fc;
border-radius: 12px;
height: 48px;
display: flex;
align-items: center;
padding: 4px 12px 4px 4px;
gap: 16px;
${utils.MediaQueries.mbDown} {
height: 48px;
width: 100%;
}
`;
const StyleSearchIconWrapper = styled__default["default"].div `
display: flex;
align-items: center;
flex-shrink: 0;
color: #8c8c8c;
svg {
width: 18px;
height: 18px;
}
`;
const StyleResultWrapper = styled__default["default"].div `
position: absolute;
top: calc(100% + 2px);
width: 100%;
border-radius: 4px;
background: ${miscTheme.theme.colors.white};
box-shadow: 0px 8px 40px 0px rgba(0, 0, 0, 0.1);
z-index: 10;
${utils.MediaQueries.mbDown} {
width: calc(100% + 64px);
left: -32px;
}
`;
const StyledSearchResultWrapper = styled__default["default"].div ``;
styled__default["default"].div `
display: flex;
justify-content: space-between;
align-item: center;
padding: 9px 14px 9px 22px;
background: #f7f9fc;
.title {
color: ${miscTheme.theme.colors.gray800};
font-size: 14px;
font-weight: 600;
line-height: 22px;
}
.btn-see-all {
color: ${miscTheme.theme.colors.blue600};
font-size: 14px;
line-height: 22px;
cursor: pointer;
}
`;
const StyleResultBody = styled__default["default"].div `
padding: 10px 16px;
max-height: 400px;
overflow: auto;
`;
const StyleResultItem = styled__default["default"].div `
display: flex;
gap: 12px;
align-items: center;
cursor: pointer;
${utils.MediaQueries.mbUp} {
&:hover {
background-color: ${miscTheme.theme.colors.blue50};
}
}
.image-item {
flex: 0 0 48px;
width: 48px;
height: 48px;
border-radius: 50%;
img {
object-fit: contain;
width: 100%;
height: 100%;
border-radius: 50%;
}
}
.infor {
overflow: hidden;
padding: 12px 0;
flex: 1;
border-bottom: 1px solid ${miscTheme.theme.colors.neutral100};
.title {
color: ${miscTheme.theme.colors.gray800};
font-size: 16px;
font-weight: 600;
line-height: 24px;
}
.description {
overflow: hidden;
color: ${miscTheme.theme.colors.gray600};
text-overflow: ellipsis;
white-space: nowrap;
font-size: 12px;
line-height: 18px;
display: flex;
gap: 8px;
align-items: center;
margin-top: 5px;
svg {
flex: 0 0 16px;
}
.sub-description {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
flex: 1;
}
}
}
&.last-item {
.infor {
border-bottom: none;
}
}
`;
styled__default["default"].div `
padding: 0 16px 16px;
.search-detail-btn {
color: ${miscTheme.theme.colors.blue600};
font-size: 14px;
line-height: 22px;
cursor: pointer;
}
`;
const SearchResult = ({ state, type, results,
// location,
isLoading, typeInformation,
// clientSlug = '',
dispatch, }) => {
const { t } = index$1.useTranslations();
const { selectedLocation } = state;
const typeSlug = React.useMemo(() => typeInformation[type].slug, [typeInformation, type]);
const handleOnClick = (item) => {
let url = '';
switch (type) {
case 'service':
url = `/${selectedLocation.slug}/${typeSlug}/${item.slug}`;
break;
case 'doctor':
case 'hospital':
url = `/${typeSlug}/${item.slug}-${item.id}`;
break;
}
if (url) {
dispatch({
type: ActionTypes.NavigateTo,
payload: url,
});
}
};
// const handleSeeAll = () => {
// let url = `/${selectedLocation.slug}/${typeSlug}`
// const slugClientUrl = MAPPING_CLIENT_WITH_SLUG[locale][clientSlug]
// if (slugClientUrl) {
// url += `/${slugClientUrl}`
// }
// if (
// (type === 'doctor' || type === 'hospital') &&
// state.selectedSpecialty.id > 0
// ) {
// url += `?specialtyId=${state.selectedSpecialty.id}`
// }
// if (url) {
// dispatch({ type: ActionTypes.NavigateTo, payload: url })
// }
// }
if (results === null)
return null;
return (React__default["default"].createElement(StyledSearchResultWrapper, null,
React__default["default"].createElement(StyleResultBody, { className: "search-results__body" },
isLoading && (React__default["default"].createElement("div", { className: "skeleton-result" }, Array(3)
.fill(undefined)
.map((_, i) => (React__default["default"].createElement(StyleResultItem, { key: type + 'loading' + i, className: i === 2 ? 'last-item' : '' },
React__default["default"].createElement("div", { className: "image-item" },
React__default["default"].createElement(core.Skeleton, { height: 48, circle: true, mb: "xl", animate: true })),
React__default["default"].createElement("div", { className: "infor" },
React__default["default"].createElement("div", { className: "title" },
React__default["default"].createElement(core.Skeleton, { height: 8, mt: 10, radius: "xl", animate: true })),
React__default["default"].createElement("div", { className: "description" },
React__default["default"].createElement("div", { className: "sub-description" },
React__default["default"].createElement(core.Skeleton, { height: 8, mt: 10, radius: "xl", width: "80%", animate: true }))))))))),
!isLoading && results && results.length > 0 && (React__default["default"].createElement(React__default["default"].Fragment, null, results.map((item, index) => {
var _a, _b;
const isLastItem = (results.length > 4 && index === 3) ||
(results.length <= 4 && index === results.length - 1);
let subDescription = '';
if (type === 'hospital') {
subDescription = (item === null || item === void 0 ? void 0 : item.address) || '';
}
if (type === 'doctor') {
subDescription = ((_a = item === null || item === void 0 ? void 0 : item.specialties) === null || _a === void 0 ? void 0 : _a.join(', ')) || '';
}
if (type === 'service') {
subDescription = ((_b = item === null || item === void 0 ? void 0 : item.hospital_branch) === null || _b === void 0 ? void 0 : _b.name) || '';
}
if (index >= 4) {
return React__default["default"].createElement(React__default["default"].Fragment, null);
}
return (React__default["default"].createElement(StyleResultItem, { key: item.id, className: core.clsx(isLastItem ? 'last-item' : '', 'result-item'), onClick: () => handleOnClick(item) },
React__default["default"].createElement("div", { className: "image-item" },
React__default["default"].createElement("img", { src: item.logo || item.avatar, alt: item.name })),
React__default["default"].createElement("div", { className: "infor" },
React__default["default"].createElement("div", { className: "title" }, item.name),
React__default["default"].createElement("div", { className: "description" },
typeInformation[type].icon,
React__default["default"].createElement("div", { className: "sub-description" }, subDescription)))));
}))),
!isLoading && (results === null || results === void 0 ? void 0 : results.length) === 0 && (React__default["default"].createElement(index$2.Text, { size: "sm", color: miscTheme.theme.colors.gray400, className: "noresult-block" }, t('booking.searchBar.noResults'))))));
};
const SearchResultContent = ({ state, type, isLoading, clientSlug, typeInformation, dispatch, }) => {
var _a;
if (state.focused !== 'search')
return null;
return (React__default["default"].createElement(SearchResult, { type: type, state: state, results: ((_a = state.searchResults) === null || _a === void 0 ? void 0 : _a[type]) || null, location: state.selectedLocation.name, dispatch: dispatch, isLoading: isLoading, typeInformation: typeInformation, clientSlug: clientSlug }));
};
const StyleSearchSpecialtyWrapper = styled__default["default"].div `
position: relative;
flex-shrink: 0;
${utils.MediaQueries.mbDown} {
width: 100%;
display: flex;
flex-direction: column;
gap: 12px;
}
&.compact {
${utils.MediaQueries.mbDown} {
width: auto;
display: block;
}
}
`;
const StyleDropdownPanel = styled__default["default"].div `
position: absolute;
top: calc(100% + 4px);
left: 0;
min-width: 264px;
background: white;
border-radius: 12px;
box-shadow: 0px 0px 1px 0px rgba(0, 0, 0, 0.04),
0px 2px 8px 0px rgba(0, 0, 0, 0.04), 0px 10px 16px 0px rgba(0, 0, 0, 0.04);
z-index: 10;
overflow: hidden;
&.mobile {
min-width: 280px;
}
`;
const StyledSpecialtyItem = styled__default["default"].div `
cursor: pointer;
padding: 12px 16px;
display: flex;
align-items: center;
gap: 12px;
outline: none;
.image-item {
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
flex-shrink: 0;
img {
width: 100%;
height: 100%;
object-fit: contain;
}
svg {
width: 24px;
height: 24px;
}
}
.item-label {
font-size: 15px;
font-weight: 400;
line-height: 24px;
letter-spacing: -0.3px;
color: ${miscTheme.theme.colors.gray800};
}
&:hover {
background-color: ${miscTheme.theme.colors.neutral50};
}
&.selected {
background-color: ${miscTheme.theme.colors.neutral50};
}
&:last-child {
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
`;
const DoctorIcon = () => (React__default["default"].createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none" },
React__default["default"].createElement("path", { d: "M8.983 14.333q-1.666 0-2.833-1.166Q4.983 12 4.983 10.333v-.366a3.48 3.48 0 0 1-2.258-1.2 3.57 3.57 0 0 1-.909-2.434V3.1a.58.58 0 0 1 .175-.425.58.58 0 0 1 .425-.175h1.4v-.167a.486.486 0 0 1 .5-.5q.217 0 .36.142a.5.5 0 0 1 .14.358v1.334a.485.485 0 0 1-.5.5.484.484 0 0 1-.5-.5V3.5h-1v2.833q0 1.1.784 1.884Q4.383 9 5.483 9t1.883-.783.784-1.884V3.5h-1v.167a.485.485 0 0 1-.5.5.484.484 0 0 1-.5-.5V2.333a.486.486 0 0 1 .5-.5q.217 0 .358.142a.5.5 0 0 1 .142.358V2.5h1.4a.58.58 0 0 1 .425.175.58.58 0 0 1 .175.425v3.233q0 1.4-.908 2.434a3.5 3.5 0 0 1-2.259 1.2v.366q0 1.25.875 2.126a2.9 2.9 0 0 0 2.125.874q1.25 0 2.125-.874a2.9 2.9 0 0 0 .875-2.126V9.25a1.74 1.74 0 0 1-.858-.609 1.6 1.6 0 0 1-.342-1.008q0-.716.5-1.216t1.2-.5q.716 0 1.217.5.5.5.5 1.216 0 .567-.342 1.008t-.875.609v1.083q0 1.667-1.167 2.834-1.166 1.166-2.833 1.166m3.5-6q.3 0 .509-.208a.67.67 0 0 0 .208-.492q0-.3-.208-.508a.7.7 0 0 0-.509-.208q-.3 0-.5.208a.7.7 0 0 0-.2.508q0 .284.2.492t.5.208", fill: "#595959" })));
const ServiceIcon = () => (React__default["default"].createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none" },
React__default["default"].createElement("path", { d: "M6.868 9.265v.974q0 .405.284.69.284.283.69.283t.69-.284.284-.69v-.973h.973q.405 0 .69-.284a.94.94 0 0 0 .284-.689.94.94 0 0 0-.284-.69.94.94 0 0 0-.69-.284h-.973v-.974a.94.94 0 0 0-.284-.69.94.94 0 0 0-.69-.284.94.94 0 0 0-.689.284.94.94 0 0 0-.285.69v.974h-.973a.94.94 0 0 0-.69.283.94.94 0 0 0-.284.69q0 .405.284.69.285.284.69.284zm-3.46 4.868q-.581 0-.994-.413A1.36 1.36 0 0 1 2 12.725v-6.65a1.4 1.4 0 0 1 .563-1.127l4.434-3.333q.369-.282.844-.282.474 0 .846.282l4.434 3.333q.264.193.413.493.15.3.15.633v6.651q0 .58-.414.995a1.36 1.36 0 0 1-.994.413zm0-1.168h8.868q.105 0 .172-.068a.23.23 0 0 0 .068-.172v-6.65a.2.2 0 0 0-.027-.11.3.3 0 0 0-.07-.085L7.983 2.554a.2.2 0 0 0-.142-.052.2.2 0 0 0-.142.052L3.266 5.88a.3.3 0 0 0-.071.086.2.2 0 0 0-.027.108v6.651q0 .105.068.172a.23.23 0 0 0 .172.068", fill: "#595959" })));
const HospitalIcon = () => (React__default["default"].createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none" },
React__default["default"].createElement("path", { d: "M8 8q.55 0 .941-.392.393-.391.392-.941 0-.55-.392-.942A1.28 1.28 0 0 0 8 5.333q-.55 0-.942.392a1.28 1.28 0 0 0-.391.942q0 .55.391.941Q7.45 8 8 8m0 4.9q2.032-1.866 3.017-3.392Q12 7.983 12 6.8q0-1.817-1.158-2.975Q9.682 2.667 8 2.667T5.158 3.825Q4 4.983 4 6.8q0 1.184.983 2.708Q5.966 11.034 8 12.9m0 1.517a.8.8 0 0 1-.5-.183q-2.433-2.151-3.633-3.992T2.667 6.8q0-2.5 1.608-3.983Q5.883 1.332 8 1.333q2.116 0 3.725 1.484T13.333 6.8q0 1.6-1.2 3.442-1.2 1.841-3.633 3.991a.8.8 0 0 1-.5.184", fill: "#8C8C8C" })));
const SpecialtyIcon = () => (React__default["default"].createElement("svg", { width: "16", height: "16", viewBox: "0 0 16 16" },
React__default["default"].createElement("path", { d: "M3.833.5a3.333 3.333 0 1 0 0 6.667 3.333 3.333 0 0 0 0-6.667M10.5.5c-.92 0-1.667.746-1.667 1.667V5.5c0 .92.746 1.667 1.667 1.667h3.333c.921 0 1.667-.746 1.667-1.667V2.167C15.5 1.246 14.754.5 13.833.5zM3.833 8.833a3.333 3.333 0 1 0 0 6.667 3.333 3.333 0 0 0 0-6.667m8.334 0a3.333 3.333 0 1 0 0 6.667 3.333 3.333 0 0 0 0-6.667", fill: "#9AA2AC" })));
const SearchSpecialty = ({ state, isMobile, onSpecialtyClick, dispatch, isCompact = false, }) => {
const { t, locale } = index$1.useTranslations();
const inputSpecialtyWrapperRef = React.useRef(null);
const searchInputRef = React.useRef(null);
const slugs = React.useMemo(() => constants.LOCALIZED_SLUGS[locale], [locale]);
const [searchValue, setSearchValue] = React.useState('');
const [opened, { close, toggle }] = hooks.useDisclosure(false);
useOutsideClick.useOutsideClick(inputSpecialtyWrapperRef, () => {
close();
setSearchValue('');
dispatch({ type: ActionTypes.ClickOutsideInputSpecialty });
});
const specialtyData = React.useMemo(() => [
{
name: t('booking.searchBar.allSpecialties'),
id: -1,
thumbnail: '',
total_doctor: 0,
slug: slugs.SPECIALTIES,
},
...state.allSpecialties,
], [state.allSpecialties, t, slugs.SPECIALTIES]);
const filteredData = React.useMemo(() => {
if (!searchValue.trim())
return specialtyData;
return specialtyData.filter((s) => s.name.toLowerCase().includes(searchValue.toLowerCase().trim()));
}, [specialtyData, searchValue]);
const selectedLabel = React.useMemo(() => {
if (!state.selectedSpecialty)
return '';
return state.selectedSpecialty.name;
}, [state.selectedSpecialty]);
const handleSelect = React.useCallback((item) => {
onSpecialtyClick({
id: item.id,
name: item.name,
slug: item.slug,
thumbnail: item.thumbnail || '',
total_doctor: 0,
});
close();
setSearchValue('');
}, [onSpecialtyClick, close]);
const handleToggle = React.useCallback(() => {
toggle();
if (opened) {
setSearchValue('');
}
}, [toggle, opened]);
return (React__default["default"].createElement(StyleSearchSpecialtyWrapper, { ref: inputSpecialtyWrapperRef, className: isCompact ? 'compact' : '' },
React__default["default"].createElement(core.Box, { onClick: handleToggle, className: opened ? 'dropdown-trigger-open' : '', sx: {
cursor: 'pointer',
position: 'relative',
width: isCompact ? (isMobile ? 96 : 160) : '100%',
flexShrink: 0,
} },
React__default["default"].createElement(core.Input, { readOnly: true, value: selectedLabel, placeholder: t('booking.searchPopup.specialties'), rightSection: React__default["default"].createElement(ChevronDown.ChevronDown, { style: { width: 20, height: 20 } }), styles: Object.assign({ input: {
cursor: 'pointer',
outline: 'none',
'&:focus': {
outline: 'none',
},
'&:not(.mantine-Textarea-input)': {
height: isCompact ? 40 : isMobile ? 40 : 46,
fontSize: isCompact ? core.rem(14) : isMobile ? core.rem(14) : core.rem(16),
paddingLeft: isCompact ? 12 : 16,
paddingRight: 30,
background: isCompact ? 'white' : 'transparent',
border: isCompact
? `1px solid ${miscTheme.theme.colors.gray200}`
: 'none',
borderRadius: 8,
letterSpacing: '-0.2px',
color: miscTheme.theme.colors.gray800,
textOverflow: 'ellipsis',
overflow: 'hidden',
whiteSpace: 'nowrap',
},
}, rightSection: {
width: 30,
pointerEvents: 'none',
} }, (!isCompact && {
wrapper: {
position: 'relative',
'&:after': {
content: '""',
width: '1px',
height: '20px',
backgroundColor: miscTheme.theme.colors.neutral100,
position: 'absolute',
right: 0,
top: '50%',
transform: 'translateY(-50%)',
},
},
})) })),
opened && (React__default["default"].createElement(StyleDropdownPanel, { className: isMobile ? 'mobile' : '' },
React__default["default"].createElement(core.Box, { sx: { padding: '12px 12px 8px' } },
React__default["default"].createElement(core.Input, { ref: searchInputRef, icon: React__default["default"].createElement(core$1.Search, { size: 18, color: miscTheme.theme.colors.gray300 }), placeholder: t('booking.searchBar.placeholder.specialty'), value: searchValue, onChange: (e) => setSearchValue(e.target.value), size: "md", styles: {
input: {
height: 40,
fontSize: core.rem(15),
paddingLeft: 36,
'&::placeholder': {
color: miscTheme.theme.colors.gray400,
},
},
icon: {
width: 36,
},
} })),
React__default["default"].createElement(core.ScrollArea.Autosize, { mah: 300 }, filteredData.length === 0 ? (React__default["default"].createElement(core.Box, { sx: {
padding: '12px 16px',
color: miscTheme.theme.colors.gray400,
fontSize: 14,
} }, t('common.noResult'))) : (filteredData.map((specialty) => {
var _a, _b;
return (React__default["default"].createElement(StyledSpecialtyItem, { key: specialty.id, tabIndex: 0, role: "option", "aria-selected": ((_a = state.selectedSpecialty) === null || _a === void 0 ? void 0 : _a.id) === specialty.id, className: ((_b = state.selectedSpecialty) === null || _b === void 0 ? void 0 : _b.id) === specialty.id
? 'selected'
: '', onClick: () => handleSelect(specialty), onKeyDown: (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handleSelect(specialty);
}
} },
React__default["default"].createElement("div", { className: "image-item" }, specialty.thumbnail ? (React__default["default"].createElement("img", { src: specialty.thumbnail, alt: specialty.name })) : (React__default["default"].createElement(SpecialtyIcon, null))),
React__default["default"].createElement("span", { className: "item-label" }, specialty.name)));
})))))));
};
const SearchInputs = React.forwardRef(({ state, type, isMobile, isLoading, initalSearchValue, typeInformation, debouncedFetchSearchResults, trackingAttributes, hideSpecialtySelection = false, clientSlug = '', onSearch, onFocusInput, onEnterSearch, dispatch, onlyShowSearchBox, onChangeSpecialty, }, ref) => {
const { t } = index$1.useTranslations();
const inputSearchWrapperRef = React.useRef(null);
const internalInputRef = React.useRef(null);
React.useImperativeHandle(ref, () => ({
focus: () => {
var _a;
(_a = internalInputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
},
}));
const gaAttributes = React.useMemo(() => {
if (trackingAttributes) {
return {
'data-event-category': trackingAttributes.dataEventCategory,
'data-event-action': trackingAttributes.dataEventAction,
'data-event-label': trackingAttributes.dataEventLabel,
};
}
else {
return {};
}
}, [trackingAttributes]);
const placeholderSearch = React.useMemo(() => {
if (type === 'doctor')
return t('booking.searchBar.placeholder.doctor');
if (type === 'service')
return t('booking.searchBar.placeholder.service');
if (type === 'hospital')
return t('booking.searchBar.placeholder.hospital');
if (type === 'specialty')
return t('booking.searchBar.placeholder.specialty');
return '';
}, [type, t]);
const showSpecialtyInSearchBar = type !== 'specialty' &&
type !== 'service' &&
!hideSpecialtySelection &&
!onlyShowSearchBox;
const styleInputUnstyled = React.useMemo(() => ({
input: {
border: 'none',
background: 'transparent',
height: '100%',
fontSize: core.rem(16),
color: miscTheme.theme.colors.gray800,
padding: '0',
'&::placeholder': {
color: miscTheme.theme.colors.gray400,
},
'&:focus': {
outline: 'none',
boxShadow: 'none',
},
[isMobile ? '@media (max-width: 767px)' : '']: isMobile
? {
fontSize: core.rem(16),
}
: {},
},
wrapper: {
flex: 1,
},
}), [isMobile]);
useOutsideClick.useOutsideClick(inputSearchWrapperRef, () => {
!isMobile && dispatch({ type: ActionTypes.ClickOutsideInputSearch });
});
const handleSearchInputChange = React.useCallback(function handleSearchInputChange(value) {
dispatch({ type: ActionTypes.ChangeInputSearch, payload: value });
if (typeof onSearch === 'function') {
return;
}
if (value.trim().length > SEARCH_KEYWORD_MIN_LENGTH[state.locale] ||
initalSearchValue ||
value.trim().length === 0) {
debouncedFetchSearchResults(value.trim(), state, type, clientSlug);
}
}, [
dispatch,
state,
type,
initalSearchValue,
debouncedFetchSearchResults,
clientSlug,
onSearch,
]);
const handleSpecialtyClick = React.useCallback(function handleSpecialtyClick(specialty) {
var _a;
dispatch({ type: ActionTypes.SelectSpecialty, payload: specialty });
onChangeSpecialty === null || onChangeSpecialty === void 0 ? void 0 : onChangeSpecialty(specialty);
!isMobile && ((_a = internalInputRef.current) === null || _a === void 0 ? void 0 : _a.focus());
}, [dispatch, onChangeSpecialty]);
const inputOnKeyDown = React.useCallback((e) => {
if (e.key === 'Enter') {
e.stopPropagation();
e.preventDefault();
onEnterSearch && onEnterSearch();
}
}, [onEnterSearch]);
return (React__default["default"].createElement(StyleSearchInputsWrapper, { ref: inputSearchWrapperRef },
React__default["default"].createElement(StyleSearchBarContainer, null,
showSpecialtyInSearchBar && (React__default["default"].createElement(SearchSpecialty, { state: state, isMobile: isMobile, dispatch: dispatch, debouncedFetchSearchResults: debouncedFetchSearchResults, onSpecialtyClick: handleSpecialtyClick, isCompact: true })),
React__default["default"].createElement(StyleSearchIconWrapper, null,
React__default["default"].createElement(core$1.Search, null)),
React__default["default"].createElement(core.Input, Object.assign({ ref: internalInputRef, variant: "unstyled", size: isMobile ? 'md' : 'lg', placeholder: placeholderSearch, styles: styleInputUnstyled, value: state.searchKeywords, name: "search-results", autoComplete: "off", onFocus: () => {
dispatch({ type: ActionTypes.FocusInputSearch });
onFocusInput === null || onFocusInput === void 0 ? void 0 : onFocusInput();
}, onChange: (event) => {
handleSearchInputChange(event.target.value);
}, onKeyDown: inputOnKeyDown }, gaAttributes))),
!isMobile && type !== 'specialty' && (React__default["default"].createElement(StyleResultWrapper, null,
React__default["default"].createElement(SearchResultContent, { type: type, state: state, dispatch: dispatch, isLoading: isLoading, typeInformation: typeInformation, clientSlug: clientSlug })))));
});
SearchInputs.displayName = 'SearchInputs';
const ModalSearchResult = (_a) => {
var { searchInputEle, closeModal, children } = _a, props = tslib_es6.__rest(_a, ["searchInputEle", "closeModal", "children"]);
const theme = core.useMantineTheme();
return (React__default["default"].createElement(index$3.Modal, Object.assign({}, props, { zIndex: 310, fullScreen: true, styles: {
content: {
borderRadius: 0,
height: '100%',
'.search-results__body': { padding: 0 },
'.result-item': {
paddingLeft: 16,
paddingRight: 16,
},
'.noresult-block': {
padding: 16,
},
'.skeleton-result': {
padding: 16,
},
},
header: { display: 'none' },
} }),
React__default["default"].createElement(core.Stack, { spacing: 16, sx: { paddingTop: 16 } },
React__default["default"].createElement(core.Flex, { align: 'center', gap: 12 },
React__default["default"].createElement(arrow.ArrowLeft, { size: 28, onClick: closeModal, color: theme.fn.primaryColor() }),
React__default["default"].createElement(core.Box, { sx: { width: '100%' } }, searchInputEle)),
children)));
};
const StyleSearchLocationWrapper = styled__default["default"].div `
position: relative;
width: 240px;
flex-shrink: 0;
${utils.MediaQueries.mbDown} {
width: 100%;
}
`;
styled__default["default"].div `
position: absolute;
top: calc(100% + 2px);
width: 100%;
border-radius: 4px;
background: ${miscTheme.theme.colors.white};
box-shadow: 0px 8px 40px 0px rgba(0, 0, 0, 0.1);
z-index: 10;
${utils.MediaQueries.mbDown} {
width: calc(100% + 64px);
left: -32px;
}
`;
const StyledLocationItem = styled__default["default"].div `
position: relative;
cursor: pointer;
padding: 12px 18px;
display: flex;
align-items: center;
gap: 14px;
overflow: hidden;
& + li::before {
content: '';
position: absolute;
left: 18px;
right: 18px;
top: 0;
height: 1px;
background-color: ${miscTheme.theme.colors.neutral100};
}
&:not([data-is-denied='true']):hover {
background-color: ${miscTheme.theme.colors.neutral50};
}
&:last-child:hover {
border-bottom-right-radius: 6px;
border-bottom-left-radius: 6px;
}
&:first-child:hover {
border-top-right-radius: 6px;
border-top-left-radius: 6px;
}
&[data-is-denied='true'] {
cursor: not-allowed;
}
`;
const StyledPinIcon = styled__default["default"](utils$1.PinIcon) `
width: 12px;
height: 18px;
flex: 0 0 12px;
`;
const CustomSelectItem = React.forwardRef((_a, ref) => {
var { label, value } = _a, others = tslib_es6.__rest(_a, ["label", "value"]);
return (React__default["default"].createElement("div", Object.assign({ ref: ref }, others),
React__default["default"].createElement(StyledLocationItem, { key: value, role: "button", tabIndex: 0, "aria-label": `Select ${label} as location`, className: "location-item" },
React__default["default"].createElement(StyledPinIcon, { color: miscTheme.theme.colors.neutral400 }),
React__default["default"].createElement(core.Text, { size: "md" }, label))));
});
const SearchLocation = ({ state, debouncedFetchCitiesByKeyword, selectedLocation, trackingAttributes, onLocationClick, dispatch, }) => {
var _a;
const { t, locale } = index$1.useTranslations();
const inputLocationWrapperRef = React.useRef(null);
const citiesData = React.useMemo(() => {
var _a;
return [
{
name: t('booking.searchBar.allLocations'),
id: constants.ID_OPTION_ALL_LOCATION,
slug: (_a = constants.LOCALIZED_SLUGS[locale]) === null || _a === void 0 ? void 0 : _a.ALL,
},
...state.cities,
];
}, [state.cities, t, locale]);
const gaAttributes = React.useMemo(() => {
if (trackingAttributes) {
return {
'data-event-category': trackingAttributes.dataEventCategory,
'data-event-action': trackingAttributes.dataEventAction,
'data-event-label': trackingAttributes.dataEventLabel,
};
}
return {};
}, [trackingAttributes]);
const handleSearchLocationInputChange = React.useCallback(function handleSearchLocationInputChange(value) {
dispatch({ type: ActionTypes.ChangeInputLocation, payload: value });
if (value.trim().length > SEARCH_KEYWORD_MIN_LENGTH[state.locale]) {
debouncedFetchCitiesByKeyword(value.trim());
}
else {
debouncedFetchCitiesByKeyword.cancel();
dispatch({ type: ActionTypes.DisplayInitialCities });
}
}, [debouncedFetchCitiesByKeyword, dispatch, state.locale]);
useOutsideClick.useOutsideClick(inputLocationWrapperRef, () => {
dispatch({ type: ActionTypes.ClickOutsideInputLocation });
});
const stateValue = (_a = selectedLocation === null || selectedLocation === void 0 ? void 0 : selectedLocation.id) !== null && _a !== void 0 ? _a : null;
const defaultValue = stateValue === null ? undefined : String(stateValue);
return (React__default["default"].createElement(StyleSearchLocationWrapper, { ref: inputLocationWrapperRef },
React__default["default"].createElement(core.Select, Object.assign({ key: defaultValue !== null && defaultValue !== void 0 ? defaultValue : 'no-selected-location', defaultValue: defaultValue, placeholder: t('booking.searchBar.allLocations'), searchable: true, onSearchChange: (value) => {
handleSearchLocationInputChange(value);
}, onFocus: () => {
dispatch({ type: ActionTypes.DisplayInitialCities });
}, onDropdownClose: () => {
debouncedFetchCitiesByKeyword.cancel();
dispatch({ type: ActionTypes.DisplayInitialCities });
}, nothingFound: t('common.noResult'), maxDropdownHeight: 340, dropdownPosition: "bottom", icon: React__default["default"].createElement("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg" },
React__default["default"].createElement("path", { d: "M9.99908 10.8332C11.2264 10.8332 12.2213 9.83764 12.2213 8.61097C12.2213 7.3843 11.2264 6.38875 9.99908 6.38875C8.77174 6.38875 7.77686 7.3843 7.77686 8.61097C7.77686 9.83764 8.77174 10.8332 9.99908 10.8332Z", stroke: "#111111", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
React__default["default"].createElement("path", { d: "M9.99919 18.0554C11.7584 17.4999 16.6659 13.611 16.6659 8.61097C16.6659 4.93097 13.6811 1.94431 9.99919 1.94431C6.3173 1.94431 3.33252 4.93097 3.33252 8.61097C3.33252 13.611 8.23996 17.4999 9.99919 18.0554Z", stroke: "#111111", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })), rightSection: React__default["default"].createElement(ChevronDown.ChevronDown, { style: { width: 20, height: 20 } }) }, gaAttributes, { styles: () => ({
item: {
padding: 0,
'&[data-selected]': {
'&, &:hover': {
backgroundColor: '#F7F9FC',
color: 'inherit',
},
'.location-item': {
'&::after': {
content: utils$1.checkMarkIconUrl,
top: '50%',
right: 19,
position: 'absolute',
transform: 'translateY(-50%)',
},
},
},
'&[data-hovered]': {
backgroundColor: 'transparent',
},
},
root: {
width: '100%',
},
wrapper: {
width: '100%',
},
input: {
'&:not(.mantine-Textarea-input)': {
height: 48,
fontSize: core.rem(15),
fontWeight: 400,
letterSpacing: '-0.3px',
color: miscTheme.theme.colors.gray1000,
paddingLeft: 44,
paddingRight: 40,
background: 'white',
border: