search.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774
  1. <template>
  2. <div>
  3. <div class="inner--headers">
  4. <h2>벤더사 검색</h2>
  5. <div class="bread--crumbs--wrap">
  6. <span>홈</span>
  7. <span>벤더사 검색</span>
  8. </div>
  9. </div>
  10. <!-- 검색 및 필터 영역 -->
  11. <div class="search--modules type2">
  12. <div class="search--inner">
  13. <div class="form--cont--filter">
  14. <v-select
  15. v-model="searchFilter.category"
  16. :items="categoryOptions"
  17. variant="outlined"
  18. class="custom-select"
  19. label="카테고리"
  20. hide-details
  21. >
  22. </v-select>
  23. </div>
  24. <div class="form--cont--filter">
  25. <v-select
  26. v-model="searchFilter.region"
  27. :items="regionOptions"
  28. variant="outlined"
  29. class="custom-select"
  30. label="지역"
  31. hide-details
  32. >
  33. </v-select>
  34. </div>
  35. <div class="form--cont--text">
  36. <v-text-field
  37. v-model="searchFilter.keyword"
  38. class="custom-input mini"
  39. style="width: 100%"
  40. placeholder="벤더사명을 입력하세요"
  41. @keyup.enter="handleSearch"
  42. ></v-text-field>
  43. </div>
  44. </div>
  45. <v-btn
  46. class="custom-btn btn-blue mini sch--btn"
  47. @click="handleSearch"
  48. :loading="loading"
  49. >
  50. <v-icon>mdi-magnify</v-icon>
  51. 검색
  52. </v-btn>
  53. </div>
  54. <!-- 파트너십 상태 탭 -->
  55. <div class="partnership--tabs">
  56. <v-tabs v-model="activeTab" class="custom-tabs">
  57. <v-tab value="new">신규 벤더사</v-tab>
  58. <v-tab value="current">현재 파트너십</v-tab>
  59. <v-tab value="rejected">거부된 요청</v-tab>
  60. <v-tab value="terminated">해지된 파트너십</v-tab>
  61. </v-tabs>
  62. </div>
  63. <!-- 검색 결과 -->
  64. <div class="vendor--grid">
  65. <div class="vendors--list">
  66. <!-- 로딩 상태 -->
  67. <div v-if="loading" class="loading-wrap">
  68. <v-progress-circular
  69. indeterminate
  70. color="primary"
  71. size="64"
  72. ></v-progress-circular>
  73. <p>검색 중...</p>
  74. </div>
  75. <!-- 검색 결과 없음 -->
  76. <div v-else-if="filteredVendors.length === 0" class="no-results">
  77. <div class="no-data">
  78. <v-icon size="64" color="grey-lighten-1">mdi-office-building-outline</v-icon>
  79. <h3>검색 결과가 없습니다</h3>
  80. <p>다른 키워드로 검색해보세요</p>
  81. </div>
  82. </div>
  83. <!-- 벤더사 카드 리스트 -->
  84. <div v-else class="vendor--cards">
  85. <div
  86. v-for="vendor in filteredVendors"
  87. :key="vendor.SEQ"
  88. class="vendor--card"
  89. :class="{ 'partnership-exists': vendor.PARTNERSHIP_STATUS }"
  90. >
  91. <!-- 벤더사 로고 -->
  92. <div class="vendor--logo">
  93. <v-img
  94. v-if="vendor.LOGO"
  95. :src="vendor.LOGO"
  96. :alt="vendor.COMPANY_NAME"
  97. width="80"
  98. height="80"
  99. cover
  100. ></v-img>
  101. <div v-else class="no-logo">
  102. {{ vendor.COMPANY_NAME?.charAt(0) || "V" }}
  103. </div>
  104. </div>
  105. <!-- 벤더사 정보 -->
  106. <div class="vendor--info">
  107. <h3 class="vendor--name">{{ vendor.COMPANY_NAME }}</h3>
  108. <div class="vendor--meta">
  109. <div v-if="vendor.CATEGORY" class="meta--item">
  110. <v-icon size="16">mdi-tag-outline</v-icon>
  111. <span>{{ getCategoryText(vendor.CATEGORY) }}</span>
  112. </div>
  113. <div v-if="vendor.REGION" class="meta--item">
  114. <v-icon size="16">mdi-map-marker-outline</v-icon>
  115. <span>{{ vendor.REGION }}</span>
  116. </div>
  117. <div class="meta--item">
  118. <v-icon size="16">mdi-handshake-outline</v-icon>
  119. <span>{{ vendor.PARTNERSHIP_COUNT || 0 }}개 파트너십</span>
  120. </div>
  121. </div>
  122. <p v-if="vendor.DESCRIPTION" class="vendor--description">
  123. {{ vendor.DESCRIPTION }}
  124. </p>
  125. <!-- 파트너십 상태 -->
  126. <div v-if="vendor.PARTNERSHIP_STATUS" class="partnership--status">
  127. <v-chip
  128. :color="getPartnershipColor(vendor.PARTNERSHIP_STATUS)"
  129. size="small"
  130. variant="tonal"
  131. >
  132. {{ getPartnershipText(vendor.PARTNERSHIP_STATUS) }}
  133. </v-chip>
  134. <!-- 거부 사유 표시 -->
  135. <div
  136. v-if="
  137. vendor.PARTNERSHIP_STATUS === 'REJECTED' && vendor.RESPONSE_MESSAGE
  138. "
  139. class="rejection--reason"
  140. >
  141. <v-alert type="error" variant="tonal" density="compact" class="mt-2">
  142. <div class="rejection--content">
  143. <strong>거부 사유:</strong>
  144. <p class="mt-1">{{ vendor.RESPONSE_MESSAGE }}</p>
  145. <small class="text-grey">{{
  146. formatDate(vendor.RESPONSE_DATE)
  147. }}</small>
  148. </div>
  149. </v-alert>
  150. </div>
  151. </div>
  152. </div>
  153. <!-- 액션 버튼 -->
  154. <div class="vendor--actions">
  155. <!-- 신규 벤더사 - 승인요청 -->
  156. <v-btn
  157. v-if="!vendor.PARTNERSHIP_STATUS"
  158. color="primary"
  159. variant="flat"
  160. size="small"
  161. @click="requestPartnership(vendor)"
  162. :loading="processing"
  163. >
  164. <v-icon left size="16">mdi-handshake</v-icon>
  165. 승인요청
  166. </v-btn>
  167. <!-- 거부된 요청 - 재승인요청 -->
  168. <v-btn
  169. v-else-if="vendor.PARTNERSHIP_STATUS === 'REJECTED'"
  170. color="orange"
  171. variant="flat"
  172. size="small"
  173. @click="requestReapply(vendor)"
  174. :loading="processing"
  175. >
  176. <v-icon left size="16">mdi-refresh</v-icon>
  177. 재승인요청
  178. </v-btn>
  179. <!-- 해지된 파트너십 - 재승인요청 -->
  180. <v-btn
  181. v-else-if="vendor.PARTNERSHIP_STATUS === 'TERMINATED'"
  182. color="success"
  183. variant="flat"
  184. size="small"
  185. @click="requestReapply(vendor)"
  186. :loading="processing"
  187. >
  188. <v-icon left size="16">mdi-refresh</v-icon>
  189. 재승인요청
  190. </v-btn>
  191. <!-- 진행중인 파트너십 -->
  192. <v-btn
  193. v-else
  194. variant="outlined"
  195. size="small"
  196. @click="viewPartnership(vendor)"
  197. >
  198. 파트너십 보기
  199. </v-btn>
  200. <!-- 상세보기 버튼 -->
  201. <v-btn variant="text" size="small" @click="viewVendorDetail(vendor.SEQ)">
  202. 상세보기
  203. </v-btn>
  204. </div>
  205. </div>
  206. </div>
  207. </div>
  208. <!-- 페이지네이션 -->
  209. <div v-if="pagination.totalPages > 1" class="pagination-wrap">
  210. <v-pagination
  211. v-model="currentPage"
  212. :length="pagination.totalPages"
  213. :total-visible="5"
  214. @update:model-value="handlePageChange"
  215. ></v-pagination>
  216. </div>
  217. </div>
  218. <!-- 승인요청 모달 -->
  219. <v-dialog v-model="requestModal.show" max-width="600px" persistent>
  220. <v-card>
  221. <v-card-title class="d-flex align-center">
  222. <v-icon class="mr-3" color="primary">mdi-handshake</v-icon>
  223. {{ requestModal.isReapply ? "재승인요청" : "파트너십 승인요청" }}
  224. </v-card-title>
  225. <v-card-text>
  226. <div class="request--content">
  227. <div class="vendor--summary">
  228. <h4>{{ requestModal.vendor?.COMPANY_NAME }}</h4>
  229. <p>
  230. {{ getCategoryText(requestModal.vendor?.CATEGORY) }} ·
  231. {{ requestModal.vendor?.REGION }}
  232. </p>
  233. </div>
  234. <v-divider class="my-4"></v-divider>
  235. <v-textarea
  236. v-model="requestModal.message"
  237. label="요청 메시지"
  238. placeholder="파트너십을 원하는 이유나 제안사항을 입력해주세요"
  239. rows="4"
  240. variant="outlined"
  241. class="mb-4"
  242. ></v-textarea>
  243. <div class="form-row">
  244. <v-text-field
  245. v-model="requestModal.commissionRate"
  246. label="희망 수수료율 (%)"
  247. type="number"
  248. variant="outlined"
  249. class="mr-2"
  250. :disabled="requestModal.isReapply"
  251. ></v-text-field>
  252. <v-text-field
  253. v-model="requestModal.specialConditions"
  254. label="특별 조건"
  255. variant="outlined"
  256. :disabled="requestModal.isReapply"
  257. ></v-text-field>
  258. </div>
  259. <div v-if="requestModal.isReapply" class="reapply--info">
  260. <v-alert type="info" variant="tonal" class="mb-3">
  261. 재승인요청 시 이전 계약 조건이 자동으로 적용됩니다.
  262. </v-alert>
  263. </div>
  264. </div>
  265. </v-card-text>
  266. <v-card-actions>
  267. <v-spacer></v-spacer>
  268. <v-btn variant="text" @click="closeRequestModal">취소</v-btn>
  269. <v-btn
  270. color="primary"
  271. variant="flat"
  272. @click="submitRequest"
  273. :loading="processing"
  274. :disabled="!requestModal.message.trim()"
  275. >
  276. {{ requestModal.isReapply ? "재승인요청" : "승인요청" }}
  277. </v-btn>
  278. </v-card-actions>
  279. </v-card>
  280. </v-dialog>
  281. </div>
  282. </template>
  283. <script setup>
  284. import { ref, computed, onMounted } from "vue";
  285. definePageMeta({
  286. layout: "default",
  287. });
  288. const { $toast } = useNuxtApp();
  289. const authStore = useAuthStore();
  290. // 반응형 데이터
  291. const loading = ref(false);
  292. const processing = ref(false);
  293. const vendors = ref([]);
  294. const currentPage = ref(1);
  295. const activeTab = ref("new");
  296. const searchFilter = ref({
  297. keyword: "",
  298. category: "",
  299. region: "",
  300. });
  301. const pagination = ref({
  302. currentPage: 1,
  303. totalPages: 1,
  304. totalCount: 0,
  305. pageSize: 12,
  306. });
  307. const requestModal = ref({
  308. show: false,
  309. vendor: null,
  310. message: "",
  311. commissionRate: "",
  312. specialConditions: "",
  313. isReapply: false,
  314. });
  315. // 옵션 데이터
  316. const categoryOptions = [
  317. { title: "전체", value: "" },
  318. { title: "패션·뷰티", value: "FASHION_BEAUTY" },
  319. { title: "식품·건강", value: "FOOD_HEALTH" },
  320. { title: "라이프스타일", value: "LIFESTYLE" },
  321. { title: "테크·가전", value: "TECH_ELECTRONICS" },
  322. { title: "스포츠·레저", value: "SPORTS_LEISURE" },
  323. { title: "문화·엔터테인먼트", value: "CULTURE_ENTERTAINMENT" },
  324. ];
  325. const regionOptions = [
  326. { title: "전체", value: "" },
  327. { title: "서울", value: "SEOUL" },
  328. { title: "경기", value: "GYEONGGI" },
  329. { title: "인천", value: "INCHEON" },
  330. { title: "부산", value: "BUSAN" },
  331. { title: "대구", value: "DAEGU" },
  332. { title: "대전", value: "DAEJEON" },
  333. { title: "광주", value: "GWANGJU" },
  334. { title: "울산", value: "ULSAN" },
  335. { title: "기타", value: "OTHER" },
  336. ];
  337. // 현재 사용자 SEQ
  338. const currentUserSeq = computed(() => authStore.getUserSeq);
  339. // 필터링된 벤더사 목록
  340. const filteredVendors = computed(() => {
  341. if (activeTab.value === "new") {
  342. return vendors.value.filter((v) => !v.PARTNERSHIP_STATUS);
  343. } else if (activeTab.value === "current") {
  344. return vendors.value.filter((v) =>
  345. ["PENDING", "APPROVED"].includes(v.PARTNERSHIP_STATUS)
  346. );
  347. } else if (activeTab.value === "rejected") {
  348. return vendors.value.filter((v) => v.PARTNERSHIP_STATUS === "REJECTED");
  349. } else if (activeTab.value === "terminated") {
  350. return vendors.value.filter((v) => v.PARTNERSHIP_STATUS === "TERMINATED");
  351. }
  352. return vendors.value;
  353. });
  354. // 메서드들
  355. const handleSearch = async () => {
  356. loading.value = true;
  357. currentPage.value = 1;
  358. try {
  359. const params = {
  360. keyword: searchFilter.value.keyword,
  361. category: searchFilter.value.category,
  362. region: searchFilter.value.region,
  363. sortBy: "latest",
  364. page: currentPage.value,
  365. size: pagination.value.pageSize,
  366. influencerSeq: currentUserSeq.value,
  367. };
  368. useAxios()
  369. .post("/api/vendor-influencer/search-vendors", params)
  370. .then((res) => {
  371. console.log("Search API Response:", res.data); // 디버깅 로그
  372. if (res.data.success) {
  373. vendors.value = res.data.data.items || [];
  374. pagination.value = res.data.data.pagination || {};
  375. console.log("Vendors set:", vendors.value.length); // 디버깅 로그
  376. console.log("Pagination set:", pagination.value); // 디버깅 로그
  377. } else {
  378. $toast.error(res.data.message || "검색에 실패했습니다.");
  379. }
  380. })
  381. .catch((err) => {
  382. $toast.error("검색 중 오류가 발생했습니다.");
  383. console.error("Search error:", err);
  384. })
  385. .finally(() => {
  386. loading.value = false;
  387. });
  388. } catch (err) {
  389. $toast.error("검색 중 오류가 발생했습니다.");
  390. loading.value = false;
  391. }
  392. };
  393. const handlePageChange = (page) => {
  394. currentPage.value = page;
  395. handleSearch();
  396. };
  397. // 파트너십 요청
  398. const requestPartnership = (vendor) => {
  399. requestModal.value = {
  400. show: true,
  401. vendor: vendor,
  402. message: "",
  403. commissionRate: "",
  404. specialConditions: "",
  405. isReapply: false,
  406. };
  407. };
  408. // 재승인요청
  409. const requestReapply = (vendor) => {
  410. // 거부된 요청인지 해지된 요청인지 구분
  411. const isRejected = vendor.PARTNERSHIP_STATUS === "REJECTED";
  412. const defaultMessage = isRejected
  413. ? "이전 요청이 거부되었지만, 조건을 수정하여 다시 승인 요청드립니다."
  414. : "재승인 요청드립니다.";
  415. requestModal.value = {
  416. show: true,
  417. vendor: vendor,
  418. message: defaultMessage,
  419. commissionRate: vendor.COMMISSION_RATE || "",
  420. specialConditions: vendor.SPECIAL_CONDITIONS || "",
  421. isReapply: true,
  422. };
  423. };
  424. const submitRequest = async () => {
  425. try {
  426. processing.value = true;
  427. const endpoint = requestModal.value.isReapply
  428. ? "/api/vendor-influencer/reapply-request"
  429. : "/api/vendor-influencer/create-request";
  430. // 디버깅 로그
  431. console.log("Current User SEQ:", currentUserSeq.value);
  432. console.log("Auth Store:", authStore);
  433. const params = {
  434. vendorSeq: requestModal.value.vendor.SEQ,
  435. influencerSeq: currentUserSeq.value,
  436. requestMessage: requestModal.value.message,
  437. requestedBy: currentUserSeq.value,
  438. ...(requestModal.value.isReapply
  439. ? {}
  440. : {
  441. commissionRate: requestModal.value.commissionRate,
  442. specialConditions: requestModal.value.specialConditions,
  443. }),
  444. };
  445. console.log("Request Params:", params);
  446. useAxios()
  447. .post(endpoint, params)
  448. .then((res) => {
  449. if (res.data.success) {
  450. $toast.success(res.data.message);
  451. closeRequestModal();
  452. handleSearch(); // 리스트 새로고침
  453. } else {
  454. $toast.error(res.data.message || "요청 처리에 실패했습니다.");
  455. }
  456. })
  457. .catch((err) => {
  458. $toast.error("요청 처리 중 오류가 발생했습니다.");
  459. console.error("Request error:", err);
  460. })
  461. .finally(() => {
  462. processing.value = false;
  463. });
  464. } catch (err) {
  465. $toast.error("요청 처리 중 오류가 발생했습니다.");
  466. processing.value = false;
  467. }
  468. };
  469. const closeRequestModal = () => {
  470. requestModal.value = {
  471. show: false,
  472. vendor: null,
  473. message: "",
  474. commissionRate: "",
  475. specialConditions: "",
  476. isReapply: false,
  477. };
  478. };
  479. const viewPartnership = (vendor) => {
  480. navigateTo(`/view/influencer/partnerships`);
  481. };
  482. const viewVendorDetail = (vendorSeq) => {
  483. navigateTo(`/view/vendor/${vendorSeq}`);
  484. };
  485. // 유틸리티 함수들
  486. const getCategoryText = (category) => {
  487. const categoryMap = {
  488. FASHION_BEAUTY: "패션·뷰티",
  489. FOOD_HEALTH: "식품·건강",
  490. LIFESTYLE: "라이프스타일",
  491. TECH_ELECTRONICS: "테크·가전",
  492. SPORTS_LEISURE: "스포츠·레저",
  493. CULTURE_ENTERTAINMENT: "문화·엔터테인먼트",
  494. };
  495. return categoryMap[category] || category || "기타";
  496. };
  497. const getPartnershipColor = (status) => {
  498. const colorMap = {
  499. PENDING: "warning",
  500. APPROVED: "success",
  501. REJECTED: "error",
  502. TERMINATED: "grey",
  503. CANCELLED: "grey",
  504. };
  505. return colorMap[status] || "grey";
  506. };
  507. const getPartnershipText = (status) => {
  508. const textMap = {
  509. PENDING: "승인 대기중",
  510. APPROVED: "파트너십 진행중",
  511. REJECTED: "승인 거부",
  512. TERMINATED: "파트너십 해지됨",
  513. CANCELLED: "요청 취소됨",
  514. };
  515. return textMap[status] || status || "알 수 없음";
  516. };
  517. // 라이프사이클
  518. onMounted(() => {
  519. handleSearch();
  520. });
  521. </script>
  522. <style scoped>
  523. .partnership--tabs {
  524. margin: 24px 0;
  525. }
  526. .vendor--grid {
  527. margin-top: 24px;
  528. }
  529. .vendor--cards {
  530. display: grid;
  531. grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
  532. gap: 24px;
  533. }
  534. .vendor--card {
  535. background: white;
  536. border-radius: 12px;
  537. padding: 24px;
  538. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  539. transition: transform 0.2s, box-shadow 0.2s;
  540. display: flex;
  541. gap: 20px;
  542. }
  543. .vendor--card:hover {
  544. transform: translateY(-4px);
  545. box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
  546. }
  547. .vendor--card.partnership-exists {
  548. border-left: 4px solid #4caf50;
  549. }
  550. .vendor--logo {
  551. width: 80px;
  552. height: 80px;
  553. border-radius: 8px;
  554. overflow: hidden;
  555. flex-shrink: 0;
  556. background: #f5f5f5;
  557. display: flex;
  558. align-items: center;
  559. justify-content: center;
  560. }
  561. .no-logo {
  562. font-size: 32px;
  563. font-weight: bold;
  564. color: #666;
  565. }
  566. .vendor--info {
  567. flex: 1;
  568. }
  569. .vendor--name {
  570. margin: 0 0 12px 0;
  571. font-size: 18px;
  572. font-weight: 600;
  573. color: #333;
  574. }
  575. .vendor--meta {
  576. display: flex;
  577. flex-wrap: wrap;
  578. gap: 12px;
  579. margin-bottom: 12px;
  580. }
  581. .meta--item {
  582. display: flex;
  583. align-items: center;
  584. gap: 4px;
  585. color: #666;
  586. font-size: 14px;
  587. }
  588. .vendor--description {
  589. font-size: 14px;
  590. line-height: 1.5;
  591. color: #666;
  592. margin: 0 0 16px 0;
  593. display: -webkit-box;
  594. -webkit-line-clamp: 2;
  595. -webkit-box-orient: vertical;
  596. overflow: hidden;
  597. }
  598. .partnership--status {
  599. margin-bottom: 16px;
  600. }
  601. .vendor--actions {
  602. display: flex;
  603. flex-direction: column;
  604. gap: 8px;
  605. flex-shrink: 0;
  606. }
  607. .request--content {
  608. padding: 8px 0;
  609. }
  610. .vendor--summary h4 {
  611. margin: 0 0 4px 0;
  612. font-size: 16px;
  613. font-weight: 600;
  614. }
  615. .vendor--summary p {
  616. margin: 0;
  617. color: #666;
  618. font-size: 14px;
  619. }
  620. .form-row {
  621. display: flex;
  622. gap: 12px;
  623. }
  624. .reapply--info {
  625. margin-top: 16px;
  626. }
  627. .loading-wrap {
  628. display: flex;
  629. flex-direction: column;
  630. align-items: center;
  631. justify-content: center;
  632. padding: 60px 20px;
  633. }
  634. .loading-wrap p {
  635. margin-top: 16px;
  636. color: #666;
  637. }
  638. .no-results {
  639. display: flex;
  640. justify-content: center;
  641. padding: 60px 20px;
  642. }
  643. .no-data {
  644. text-align: center;
  645. }
  646. .no-data h3 {
  647. margin: 16px 0 8px;
  648. color: #666;
  649. }
  650. .no-data p {
  651. color: #999;
  652. }
  653. .pagination-wrap {
  654. display: flex;
  655. justify-content: center;
  656. margin-top: 32px;
  657. }
  658. .rejection--content {
  659. font-size: 0.875rem;
  660. }
  661. .rejection--content p {
  662. margin: 4px 0;
  663. line-height: 1.4;
  664. }
  665. .rejection--content small {
  666. font-size: 0.75rem;
  667. opacity: 0.8;
  668. }
  669. @media (max-width: 768px) {
  670. .vendor--cards {
  671. grid-template-columns: 1fr;
  672. }
  673. .vendor--card {
  674. flex-direction: column;
  675. text-align: center;
  676. }
  677. .vendor--actions {
  678. flex-direction: row;
  679. justify-content: center;
  680. }
  681. .form-row {
  682. flex-direction: column;
  683. }
  684. }
  685. </style>