You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

294 lines
7.5KB

  1. import { AxiosError, AxiosResponse } from "axios";
  2. import { format } from "date-fns";
  3. import { Dictionary, get } from "lodash";
  4. import { DataUrl } from "@types";
  5. import { setFormErrorMessages } from "components/hook-form";
  6. import axios from "utils/axios";
  7. let id = 0;
  8. export const ApiId = {
  9. // 共通---------------------------------------
  10. CSRF_TOKEN: id++,
  11. // 認証関連 ----------------------------------
  12. ME: id++,
  13. LOGIN: id++,
  14. LOGOUT: id++,
  15. PASSWORD_SETTING_START: id++,
  16. PASSWORD_SETTING_VERIFY: id++,
  17. // 定期関連---------------------------------
  18. SEASON_TICKET_CONTRACTS: id++,
  19. PAYMENT_PLANS: id++,
  20. SEASON_TICKET_CONTRACT_STICKER_RE_ORDER: id++,
  21. SEASON_TICKET_CONTRACT_SEASON_TICKET_RE_ORDER: id++,
  22. SEASON_TICKET_CONTRACT_PARKING_CERTIFICATE_ORDER: id++,
  23. SEASON_TICKET_CONTRACT_TERMINATE_ORDER: id++,
  24. SEASON_TICKET_CONTRACT_TERMINATE_ORDER_OPTIONS: id++,
  25. SEASON_TICKET_CONTRACT_UPDATE_VEHICLE_INFO_ORDER: id++,
  26. SEASON_TICKET_CONTRACT_UPDATE_VEHICLE_INFO_ORDER_PARAMS: id++,
  27. SEASON_TICKET_CONTRACT_CHANGE_PLAN_ORDER: id++,
  28. SEASON_TICKET_CONTRACT_CHANGE_PLAN_ORDER_PARAMS: id++,
  29. SEASON_TICKET_CONTRACT_UPLOAD_STUDENT_LICENSE_IMAGES: id++,
  30. SEASON_TICKET_CONTRACT_UPLOAD_OTHER_LICENSE_IMAGES: id++,
  31. // 定期申込関連---------------------------------
  32. SEASON_TICKET_CONTRACT_ENTRY_INFO: id++,
  33. SEASON_TICKET_CONTRACT_ENTRY_CANCEL: id++,
  34. SEASON_TICKET_CONTRACT_SELECTION_INFO: id++,
  35. SEASON_TICKET_CONTRACT_SELECTION_ENTRY: id++,
  36. // 領収証関連---------------------------------
  37. RECEIPTS: id++,
  38. // 問い合わせ関連-------------------------------
  39. FAQ: id++,
  40. FAQ_GENRES: id++,
  41. ASK: id++,
  42. // 利用者情報関連-------------------------------
  43. START_CHANGE_EMAIL: id++,
  44. VERIFY_CHANGE_EMAIL: id++,
  45. CUSTOMER_UPDATE_INFO_ORDER: id++,
  46. CUSTOMER_REGISTER_BANK_ACCOUNT_START_PARAM: id++,
  47. } as const;
  48. export type ApiId = (typeof ApiId)[keyof typeof ApiId];
  49. export const HttpMethod = {
  50. GET: "get",
  51. POST: "post",
  52. } as const;
  53. export type HttpMethod = (typeof HttpMethod)[keyof typeof HttpMethod];
  54. export const ResultCode = {
  55. SUCCESS: 0,
  56. FAILED: 1,
  57. UNAUTHORIZED: 2,
  58. EXCLUSIVE_ERROR: 3,
  59. };
  60. export type ResultCode = (typeof ResultCode)[keyof typeof ResultCode];
  61. export interface TimestampRequest {
  62. timestamp: string;
  63. }
  64. export type ListRequest = {
  65. sort?: string;
  66. order?: string;
  67. };
  68. export interface APICommonResponse {
  69. result: ResultCode;
  70. messages: {
  71. errors?: Dictionary<string>;
  72. general?: string;
  73. email_id?: number;
  74. };
  75. }
  76. export type ImagesResponse = {
  77. data: {
  78. images: DataUrl[];
  79. };
  80. } & APICommonResponse;
  81. export const makeParam = <T extends object>(data: T): Dictionary<string> => {
  82. const res: Dictionary<string> = {};
  83. Object.keys(data).map((key) => {
  84. const val = get(data, key);
  85. if (typeof val === "string" && val.length !== 0) {
  86. res[key] = val;
  87. } else if (typeof val === "number") {
  88. res[key] = String(val);
  89. } else if (typeof val === "boolean") {
  90. res[key] = val ? "1" : "0";
  91. } else if (val instanceof Date) {
  92. res[key] = format(val, "yyyy-MM-dd");
  93. }
  94. });
  95. return res;
  96. };
  97. export const makeFormData = <T extends object>(data: T): FormData => {
  98. const res = new FormData();
  99. Object.keys(data).map((key) => {
  100. const val = get(data, key);
  101. if (typeof val === "string" && val.length !== 0) {
  102. res.append(key, val);
  103. } else if (typeof val === "number") {
  104. res.append(key, String(val));
  105. } else if (typeof val === "boolean") {
  106. res.append(key, val ? "1" : "0");
  107. } else if (val instanceof Date) {
  108. res.append(key, format(val, "yyyy-MM-dd"));
  109. } else if (val instanceof File) {
  110. res.append(key, val);
  111. } else if (Array.isArray(val)) {
  112. val.forEach((v) => {
  113. res.append(key + "[]", v);
  114. });
  115. } else {
  116. console.log("undefined data", key, val);
  117. }
  118. });
  119. return res;
  120. };
  121. const isAxiosError = (error: any): error is AxiosError => {
  122. return !!error.isAxiosError;
  123. };
  124. type RequestArgument = {
  125. url: string;
  126. method: HttpMethod;
  127. data?: URLSearchParams | FormData | object;
  128. multipart?: boolean;
  129. };
  130. export const request = async <T extends APICommonResponse>({
  131. url,
  132. method,
  133. data,
  134. multipart,
  135. }: RequestArgument): Promise<T | null> => {
  136. let response: AxiosResponse<T | any> | null = null;
  137. if (data instanceof URLSearchParams) {
  138. data.sort();
  139. }
  140. try {
  141. if (multipart && data instanceof FormData) {
  142. response = await axios({
  143. url,
  144. method: "post",
  145. data,
  146. headers: {
  147. "content-type": "multipart/form-data",
  148. },
  149. });
  150. console.log("RESPONSE", url, "multipart", data, response?.data);
  151. } else if (method === HttpMethod.GET) {
  152. let searchUrl = url;
  153. if (data instanceof URLSearchParams) {
  154. searchUrl += "?" + data.toString();
  155. }
  156. response = await axios.get<T>(searchUrl);
  157. console.log(new Date(), "RESPONSE", searchUrl, method, response?.data);
  158. } else if (method === HttpMethod.POST) {
  159. response = await axios.post<T>(url, data);
  160. let sendData: Dictionary<String> = {};
  161. if (data instanceof URLSearchParams) {
  162. data.forEach((val, key) => {
  163. sendData[key] = val;
  164. });
  165. } else if (typeof data === "object" && !(data instanceof FormData)) {
  166. Object.keys(data).forEach((key) => {
  167. sendData[key] = get(data, key);
  168. });
  169. }
  170. console.log("RESPONSE", url, method, sendData, response?.data);
  171. } else {
  172. return null;
  173. }
  174. if (response && response.data.result === ResultCode.SUCCESS) {
  175. return response.data;
  176. }
  177. return response?.data;
  178. } catch (e) {
  179. if (isAxiosError(e)) {
  180. if (e.response?.status === 401 || e.response?.status === 419) {
  181. window.location.reload();
  182. }
  183. }
  184. if (typeof e === "object") {
  185. const message = get(e, "message");
  186. if (message === "Unauthenticated.") {
  187. console.log("401!!!");
  188. window.location.reload();
  189. return null;
  190. }
  191. if (message === "CSRF token mismatch.") {
  192. console.log("419!!!");
  193. window.location.reload();
  194. return null;
  195. }
  196. if (message === "Service Unavailable") {
  197. console.log("503!!!");
  198. window.location.reload();
  199. return null;
  200. }
  201. }
  202. throw e;
  203. }
  204. };
  205. export async function apiRequest<
  206. T extends APICommonResponse,
  207. U extends object
  208. >({
  209. apiMethod,
  210. sendData,
  211. onSuccess,
  212. onFailed,
  213. onFinaly,
  214. errorSetter,
  215. setSending,
  216. }: {
  217. apiMethod: (sendData: U) => Promise<T | null>;
  218. sendData: U;
  219. onSuccess?: (res: T, sendData: U) => void;
  220. onFailed?: (res: APICommonResponse | null) => void;
  221. onFinaly?: (res: APICommonResponse | null) => void;
  222. errorSetter?: any;
  223. setSending?: (sending: boolean) => void;
  224. }) {
  225. if (setSending) {
  226. setSending(true);
  227. }
  228. try {
  229. const res = await apiMethod(sendData);
  230. if (setSending) {
  231. setSending(false);
  232. }
  233. if (res?.result === ResultCode.SUCCESS) {
  234. if (onSuccess) {
  235. onSuccess(res, sendData);
  236. }
  237. } else {
  238. if (res?.messages.errors) {
  239. if (errorSetter) {
  240. const errorCount = setFormErrorMessages(
  241. sendData,
  242. errorSetter,
  243. res.messages.errors
  244. );
  245. console.log("FormErrorCount", errorCount);
  246. }
  247. }
  248. if (onFailed) {
  249. onFailed(res);
  250. }
  251. if (onFinaly) {
  252. onFinaly(res);
  253. }
  254. }
  255. return res;
  256. } catch (e) {
  257. if (setSending) {
  258. setSending(false);
  259. }
  260. console.error(e);
  261. if (onFailed) {
  262. onFailed(null);
  263. }
  264. if (onFinaly) {
  265. onFinaly(null);
  266. }
  267. return null;
  268. }
  269. }