search.vue 21 KB

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