Manage.tsx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. 'use client'
  2. import { useState, MouseEvent, Dispatch, SetStateAction } from 'react'
  3. import PlanCards from '@/components/Plan'
  4. import { Plan, Subscription, SubscriptionState } from '@/types'
  5. import clsx from 'clsx'
  6. export const UpdateBillingLink = ({ subscription, elementType }:
  7. { subscription: SubscriptionState, elementType?: string }
  8. ) => {
  9. const [isMutating, setIsMutating] = useState(false)
  10. async function openUpdateModal(e: MouseEvent<HTMLAnchorElement>) {
  11. e.preventDefault()
  12. setIsMutating(true)
  13. /* Send request */
  14. const res = await fetch(`/api/subscriptions/${subscription?.id}`)
  15. const result = await res.json()
  16. if (result.error) {
  17. alert(result.message)
  18. setIsMutating(false)
  19. } else {
  20. LemonSqueezy.Url.Open(result.subscription.update_billing_url)
  21. setIsMutating(false)
  22. }
  23. }
  24. if (elementType == 'button') {
  25. return (
  26. <a
  27. href=""
  28. onClick={openUpdateModal}
  29. className={clsx('px-6 py-2 font-bold btn btn-block', {
  30. disabled: isMutating
  31. })}
  32. >
  33. Update your payment method
  34. </a>
  35. )
  36. } else {
  37. return (
  38. <a
  39. href=""
  40. onClick={openUpdateModal}
  41. className={clsx('mb-2', {
  42. disabled: isMutating
  43. })}
  44. >
  45. Update your payment method
  46. </a>
  47. )
  48. }
  49. }
  50. export const CancelLink = ({ subscription, setSubscription }:
  51. { subscription: SubscriptionState, setSubscription: Dispatch<SetStateAction<SubscriptionState>> }
  52. ) => {
  53. const [isMutating, setIsMutating] = useState(false)
  54. async function handleCancel(e: MouseEvent<HTMLAnchorElement>) {
  55. e.preventDefault()
  56. if (confirm(`Please confirm you want to cancel your subscription.`)) {
  57. setIsMutating(true)
  58. /* Send request */
  59. const res = await fetch(`/api/subscriptions/${subscription?.id}`, {
  60. method: 'POST',
  61. body: JSON.stringify({
  62. action: 'cancel'
  63. })
  64. })
  65. const result = await res.json()
  66. if (result.error) {
  67. alert(result.message)
  68. setIsMutating(false)
  69. } else {
  70. setSubscription({
  71. ...subscription,
  72. status: result['subscription']['status'],
  73. expiryDate: result['subscription']['ends_at'],
  74. })
  75. alert('Your subscription has been cancelled.')
  76. }
  77. }
  78. }
  79. return (
  80. <a
  81. href=""
  82. onClick={handleCancel}
  83. className={clsx('mb-2', {
  84. disabled: isMutating
  85. })}
  86. >
  87. Cancel
  88. </a>
  89. )
  90. }
  91. export const ResumeButton = ({ subscription, setSubscription }:
  92. { subscription: SubscriptionState, setSubscription: Dispatch<SetStateAction<SubscriptionState>> }
  93. ) => {
  94. const [isMutating, setIsMutating] = useState(false)
  95. const resumeSubscription = async (e: MouseEvent<HTMLAnchorElement>) => {
  96. e.preventDefault()
  97. if (confirm(`Please confirm you want to resume your subscription. You will be charged the regular subscription fee.`)) {
  98. setIsMutating(true)
  99. /* Send request */
  100. const res = await fetch(`/api/subscriptions/${subscription?.id}`, {
  101. method: 'POST',
  102. body: JSON.stringify({
  103. action: 'resume'
  104. })
  105. })
  106. const result = await res.json()
  107. if (result.error) {
  108. alert(result.message)
  109. setIsMutating(false)
  110. } else {
  111. setSubscription({
  112. ...subscription,
  113. status: result['subscription']['status'],
  114. renewalDate: result['subscription']['renews_at'],
  115. })
  116. alert('Your subscription is now active again!.')
  117. }
  118. }
  119. }
  120. return (
  121. <a
  122. href=""
  123. onClick={resumeSubscription}
  124. className={clsx('px-6 py-2 font-bold btn btn-block', {
  125. disabled: isMutating
  126. })}
  127. >
  128. Resume your subscription
  129. </a>
  130. )
  131. }
  132. export const PauseLink = ({ subscription, setSubscription }:
  133. { subscription: SubscriptionState, setSubscription: Dispatch<SetStateAction<SubscriptionState>> }
  134. ) => {
  135. const [isMutating, setIsMutating] = useState(false)
  136. async function handlePause(e: MouseEvent<HTMLAnchorElement>) {
  137. e.preventDefault()
  138. if (confirm(`Please confirm you want to pause your subscription.`)) {
  139. setIsMutating(true)
  140. /* Send request */
  141. const res = await fetch(`/api/subscriptions/${subscription?.id}`, {
  142. method: 'POST',
  143. body: JSON.stringify({
  144. action: 'pause'
  145. })
  146. })
  147. const result = await res.json()
  148. if (result.error) {
  149. alert(result.message)
  150. setIsMutating(false)
  151. } else {
  152. setSubscription({
  153. ...subscription,
  154. status: result['subscription']['status'],
  155. unpauseDate: result['subscription']['resumes_at'],
  156. })
  157. alert('Your subscription has been paused.')
  158. }
  159. }
  160. }
  161. return (
  162. <a
  163. href=""
  164. className={clsx('mb-2', {
  165. disabled: isMutating
  166. })}
  167. onClick={handlePause}
  168. >
  169. Pause payments
  170. </a>
  171. )
  172. }
  173. export const UnpauseButton = ({ subscription, setSubscription }:
  174. { subscription: SubscriptionState, setSubscription: Dispatch<SetStateAction<SubscriptionState>> }
  175. ) => {
  176. const [isMutating, setIsMutating] = useState(false)
  177. const unpauseSubscription = async (e: MouseEvent<HTMLAnchorElement>) => {
  178. e.preventDefault()
  179. if (confirm(`Please confirm you want to unpause your subscription. Your payments will reactivate on their original schedule.`)) {
  180. setIsMutating(true)
  181. /* Send request */
  182. const res = await fetch(`/api/subscriptions/${subscription?.id}`, {
  183. method: 'POST',
  184. body: JSON.stringify({
  185. action: 'unpause'
  186. })
  187. })
  188. const result = await res.json()
  189. if (result.error) {
  190. alert(result.message)
  191. setIsMutating(false)
  192. } else {
  193. setSubscription({
  194. ...subscription,
  195. status: result['subscription']['status'],
  196. renewalDate: result['subscription']['renews_at'],
  197. })
  198. alert('Your subscription is now active again!')
  199. }
  200. }
  201. }
  202. return (
  203. <a href=""
  204. onClick={unpauseSubscription}
  205. className={clsx('px-6 py-2 font-bold btn btn-block', {
  206. disabled: isMutating
  207. })}
  208. >
  209. Unpause your subscription
  210. </a>
  211. )
  212. }
  213. export const PlansComponent = ({ plans, sub }:
  214. { plans: Plan[], sub: Subscription }
  215. ) => {
  216. const [subscription, setSubscription] = useState<SubscriptionState>(() => {
  217. if (sub) {
  218. return {
  219. id: sub.lemonSqueezyId,
  220. planName: sub.plan?.variantName,
  221. planInterval: sub.plan?.interval,
  222. productId: sub.plan?.productId,
  223. variantId: sub.plan?.variantId,
  224. status: sub.status,
  225. renewalDate: sub.renewsAt,
  226. trialEndDate: sub.trialEndsAt,
  227. expiryDate: sub.endsAt,
  228. }
  229. } else {
  230. return undefined
  231. }
  232. })
  233. return (
  234. <PlanCards plans={plans} subscription={subscription} setSubscription={setSubscription} />
  235. )
  236. }