SettingsDomain.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import { Flex } from "reflexbox/styled-components";
  2. import React, { FC, useState } from "react";
  3. import styled from "styled-components";
  4. import { useStoreState, useStoreActions } from "../../store";
  5. import { useFormState } from "react-use-form-state";
  6. import { Domain } from "../../store/settings";
  7. import { useMessage } from "../../hooks";
  8. import { Colors } from "../../consts";
  9. import TextInput from "../TextInput";
  10. import { Button } from "../Button";
  11. import Table from "../Table";
  12. import Modal from "../Modal";
  13. import Icon from "../Icon";
  14. import Text, { H2, Span } from "../Text";
  15. import { Col } from "../Layout";
  16. const Th = styled(Flex).attrs({ as: "th", py: 3, px: 3 })`
  17. font-size: 15px;
  18. `;
  19. const Td = styled(Flex).attrs({ as: "td", py: 12, px: 3 })`
  20. font-size: 15px;
  21. `;
  22. const SettingsDomain: FC = () => {
  23. const [modal, setModal] = useState(false);
  24. const [loading, setLoading] = useState(false);
  25. const [deleteLoading, setDeleteLoading] = useState(false);
  26. const [domainToDelete, setDomainToDelete] = useState<Domain>(null);
  27. const [message, setMessage] = useMessage(2000);
  28. const domains = useStoreState(s => s.settings.domains);
  29. const { saveDomain, deleteDomain } = useStoreActions(s => s.settings);
  30. const [formState, { label, text }] = useFormState<{
  31. customDomain: string;
  32. homepage: string;
  33. }>(null, { withIds: true });
  34. const onSubmit = async e => {
  35. e.preventDefault();
  36. setLoading(true);
  37. try {
  38. await saveDomain(formState.values);
  39. } catch (err) {
  40. setMessage(err?.response?.data?.error || "Couldn't add domain.");
  41. }
  42. formState.clear();
  43. setLoading(false);
  44. };
  45. const closeModal = () => {
  46. setDomainToDelete(null);
  47. setModal(false);
  48. };
  49. const onDelete = async () => {
  50. setDeleteLoading(true);
  51. try {
  52. await deleteDomain();
  53. setMessage("Domain has been deleted successfully.", "green");
  54. } catch (err) {
  55. setMessage(err?.response?.data?.error || "Couldn't delete the domain.");
  56. }
  57. closeModal();
  58. setDeleteLoading(false);
  59. };
  60. return (
  61. <Col alignItems="flex-start">
  62. <H2 mb={4} bold>
  63. Custom domain
  64. </H2>
  65. <Text mb={3}>
  66. You can set a custom domain for your short URLs, so instead of{" "}
  67. <b>kutt.it/shorturl</b> you can have <b>example.com/shorturl.</b>
  68. </Text>
  69. <Text mb={4}>
  70. Point your domain A record to <b>192.64.116.170</b> then add the domain
  71. via form below:
  72. </Text>
  73. {domains.length ? (
  74. <Table my={3}>
  75. <thead>
  76. <tr>
  77. <Th width={2 / 5}>Domain</Th>
  78. <Th width={2 / 5}>Homepage</Th>
  79. <Th width={1 / 5}></Th>
  80. </tr>
  81. </thead>
  82. <tbody>
  83. {domains.map(d => (
  84. <tr key={d.customDomain}>
  85. <Td width={2 / 5}>{d.customDomain}</Td>
  86. <Td width={2 / 5}>{d.homepage || "default"}</Td>
  87. <Td width={1 / 5} justifyContent="center">
  88. <Icon
  89. as="button"
  90. name="trash"
  91. stroke={Colors.TrashIcon}
  92. strokeWidth="2.5"
  93. backgroundColor={Colors.TrashIconBg}
  94. py={0}
  95. px={0}
  96. size={[23, 24]}
  97. p={["4px", "5px"]}
  98. onClick={() => {
  99. setDomainToDelete(d);
  100. setModal(true);
  101. }}
  102. />
  103. </Td>
  104. </tr>
  105. ))}
  106. </tbody>
  107. </Table>
  108. ) : (
  109. <Col
  110. alignItems="flex-start"
  111. onSubmit={onSubmit}
  112. width={1}
  113. as="form"
  114. my={4}
  115. >
  116. <Flex width={1}>
  117. <Col mr={2} flex="1 1 auto">
  118. <Text {...label("customDomain")} as="label" mb={3} bold>
  119. Domain
  120. </Text>
  121. <TextInput
  122. {...text("customDomain")}
  123. placeholder="example.com"
  124. height={44}
  125. pl={24}
  126. pr={24}
  127. required
  128. />
  129. </Col>
  130. <Col ml={2} flex="1 1 auto">
  131. <Text {...label("homepage")} as="label" mb={3} bold>
  132. Homepage (optional)
  133. </Text>
  134. <TextInput
  135. {...text("homepage")}
  136. placeholder="Homepage URL"
  137. flex="1 1 auto"
  138. height={44}
  139. pl={24}
  140. pr={24}
  141. />
  142. </Col>
  143. </Flex>
  144. <Button type="submit" color="purple" mt={3} disabled={loading}>
  145. <Icon name={loading ? "spinner" : "plus"} mr={2} stroke="white" />
  146. {loading ? "Setting..." : "Set domain"}
  147. </Button>
  148. </Col>
  149. )}
  150. <Text color={message.color}>{message.text}</Text>
  151. <Modal id="delete-custom-domain" show={modal} closeHandler={closeModal}>
  152. <H2 mb={24} textAlign="center" bold>
  153. Delete domain?
  154. </H2>
  155. <Text textAlign="center">
  156. Are you sure do you want to delete the domain{" "}
  157. <Span bold>"{domainToDelete && domainToDelete.customDomain}"</Span>?
  158. </Text>
  159. <Flex justifyContent="center" mt={44}>
  160. {deleteLoading ? (
  161. <>
  162. <Icon name="spinner" size={20} stroke={Colors.Spinner} />
  163. </>
  164. ) : (
  165. <>
  166. <Button color="gray" mr={3} onClick={closeModal}>
  167. Cancel
  168. </Button>
  169. <Button color="red" ml={3} onClick={onDelete}>
  170. <Icon name="trash" stroke="white" mr={2} />
  171. Delete
  172. </Button>
  173. </>
  174. )}
  175. </Flex>
  176. </Modal>
  177. </Col>
  178. );
  179. };
  180. export default SettingsDomain;