uselotus__lotus METASEP

src/assets/images/logo_small.svg METASEP
<svg width="256" height="256" viewBox="0 0 256 256" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="256" height="256" fill="white"/>
<g clip-path="url(#clip0_2_2)">
<path d="M114.969 155.429C67.885 150.211 29.51 116.077 17.9651 71.1546C16.9659 67.2613 16.1557 63.2733 15.5751 59.2178C15.3725 57.8389 15.1835 56.4194 15.035 55C15.5481 55.2163 16.0477 55.4461 16.5608 55.6624C17.9921 56.2843 19.4234 56.9331 20.8412 57.5956C63.078 77.3596 96.7946 112.305 114.969 155.429Z" fill="black"/>
<path d="M160.514 158.714C134.035 172.895 103.803 180.925 71.6793 180.925C67.6825 180.925 63.6991 180.803 59.7698 180.56C45.1193 170.191 33.0477 156.416 24.6895 140.383C24.6895 140.383 24.676 140.369 24.676 140.356C20.3686 130.812 16.8308 120.862 14.1438 110.561C10.1334 95.2581 8.0135 79.2117 8.0135 62.6515C8.0135 60.813 8.04051 58.988 8.09452 57.163C8.9317 64.2061 10.363 71.06 12.3614 77.6706C13.6037 81.8072 15.0755 85.8493 16.7363 89.7831C33.8309 130.163 72.3005 159.282 117.94 162.918C121.127 163.175 124.34 163.297 127.595 163.297C130.849 163.297 134.089 163.175 137.276 162.905C145.256 162.256 153.034 160.836 160.514 158.714Z" fill="black"/>
<path d="M247 62.6515C247 68.9106 246.703 75.0885 246.109 81.1854C245.123 91.3378 243.327 101.274 240.789 110.899C240.789 110.913 240.789 110.926 240.789 110.94C238.129 121.065 234.631 130.866 230.405 140.248C214.283 171.286 184.239 193.93 148.551 199.851C148.537 199.851 148.51 199.851 148.497 199.864C144.298 200.324 140.058 200.649 135.791 200.824C133.117 200.946 130.43 201 127.73 201H127.527C124.367 201 121.221 200.919 118.102 200.757C114.443 200.581 110.811 200.297 107.205 199.905C94.6611 197.904 82.7921 193.849 71.9764 188.09C128.459 188.009 179.337 164 215.025 125.621C215.079 125.567 215.147 125.499 215.201 125.445C216.16 124.431 217.091 123.417 217.996 122.363C226.354 112.859 233.227 102.031 238.277 90.2293C239.952 86.3089 241.437 82.2804 242.693 78.1437C244.07 73.6691 245.177 69.0863 246.028 64.4089C246.406 62.3406 246.716 60.2587 246.987 58.1633C247.014 59.407 247.041 60.6643 247.041 61.9215C247 62.1513 247 62.4081 247 62.6515Z" fill="black"/>
<path d="M240.114 55.0135C239.952 56.5817 239.749 58.1363 239.519 59.6909C238.925 63.733 238.102 67.6939 237.089 71.5872C232.525 89.0396 223.924 104.843 212.325 117.942C211.798 118.524 211.285 119.105 210.758 119.673C192.746 139.18 168.008 152.347 140.22 155.429C158.381 112.359 192.017 77.4543 234.159 57.6767C235.563 57.0143 236.981 56.3654 238.412 55.7435C238.979 55.5002 239.546 55.2569 240.114 55.0135Z" fill="black"/>
<path d="M127.703 149.102C138.583 149.102 147.403 134.504 147.403 116.496C147.403 98.4876 138.583 83.8891 127.703 83.8891C116.822 83.8891 108.002 98.4876 108.002 116.496C108.002 134.504 116.822 149.102 127.703 149.102Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_2_2">
<rect width="239" height="146" fill="white" transform="translate(8 55)"/>
</clipPath>
</defs>
</svg>

src/assets/images/logo_large.jpg METASEP

src/assets/images/corner_logo.svg METASEP
<svg width="345" height="104" viewBox="0 0 345 104" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="345" height="104" fill="white"/>
<g clip-path="url(#clip0_97_2)">
<path d="M76.2665 64.344C55.9039 62.1057 39.3099 47.4304 34.3049 28.1005C33.8729 26.4254 33.5303 24.7207 33.2771 22.9715C33.1877 22.3638 33.1132 21.7708 33.0536 21.1483C33.2771 21.2372 33.4856 21.341 33.7091 21.4299C34.3347 21.6967 34.9454 21.9784 35.5561 22.26C53.8184 30.7687 68.4015 45.7998 76.2665 64.344Z" fill="black"/>
<path d="M95.9736 65.7523C84.5187 71.8447 71.4402 75.2986 57.5424 75.2986C55.8145 75.2986 54.0866 75.2393 52.3884 75.1356C46.0577 70.6737 40.8293 64.7443 37.2096 57.8513V57.8365C35.3476 53.7304 33.8133 49.4464 32.6515 45.029C30.9235 38.4622 30 31.5692 30 24.4539C30 23.6683 30.0149 22.8826 30.0298 22.097C30.3873 25.121 31.0129 28.0709 31.8769 30.917C32.4131 32.6958 33.0536 34.4302 33.7686 36.12C41.1719 53.4784 57.8105 66.0043 77.5475 67.5607C78.9179 67.6645 80.3181 67.7238 81.7183 67.7238C83.1334 67.7238 84.5336 67.6645 85.9041 67.5607C89.3748 67.2791 92.7412 66.6713 95.9736 65.7523Z" fill="black"/>
<path d="M133.392 24.4539C133.392 27.137 133.258 29.8052 133.005 32.429C132.573 36.8019 131.798 41.0711 130.696 45.2069V45.2217C129.549 49.5798 128.029 53.7897 126.197 57.8217C119.226 71.1629 106.237 80.9019 90.7899 83.4367C90.775 83.4367 90.775 83.4367 90.7601 83.4367C88.9428 83.6295 87.1106 83.7777 85.2635 83.8518C84.1017 83.9111 82.9398 83.9259 81.7779 83.9259H81.6885C80.3181 83.9259 78.9626 83.8963 77.6071 83.8222C76.0281 83.748 74.4492 83.6294 72.9 83.4516C67.4779 82.5918 62.3389 80.8426 57.6616 78.3671C82.0907 78.3375 104.107 68.0054 119.539 51.5069C119.569 51.4772 119.584 51.4476 119.613 51.4327C120.03 51.0029 120.433 50.5582 120.82 50.1134C124.44 46.0222 127.404 41.3676 129.594 36.2979C130.323 34.608 130.964 32.8737 131.5 31.1097C132.096 29.1826 132.573 27.2111 132.945 25.2099C133.109 24.3205 133.243 23.4311 133.362 22.5269C133.377 23.0605 133.377 23.609 133.377 24.1426C133.392 24.2316 133.392 24.3353 133.392 24.4539Z" fill="black"/>
<path d="M130.413 21.1631C130.338 21.8301 130.249 22.512 130.16 23.1791C129.906 24.9134 129.549 26.6181 129.102 28.2932C127.121 35.7939 123.397 42.5831 118.392 48.216C118.168 48.468 117.945 48.72 117.707 48.9572C109.916 57.3473 99.2209 63.0099 87.2 64.3292C95.0501 45.8294 109.603 30.8132 127.836 22.3045C128.447 22.0229 129.057 21.7412 129.683 21.4744C129.921 21.3706 130.16 21.2669 130.413 21.1631Z" fill="black"/>
<path d="M81.7779 61.6313C86.4836 61.6313 90.2983 55.353 90.2983 47.6083C90.2983 39.8635 86.4836 33.5852 81.7779 33.5852C77.0722 33.5852 73.2575 39.8635 73.2575 47.6083C73.2575 55.353 77.0722 61.6313 81.7779 61.6313Z" fill="black"/>
<path d="M175.502 69.4136H151.565V36.4461H159.013V62.3576H175.502V69.4136V69.4136Z" fill="black"/>
<path d="M181.014 53.004C181.014 48.216 182.667 44.1692 185.989 40.8635C189.341 37.5282 193.452 35.868 198.323 35.868C203.194 35.868 207.29 37.5282 210.612 40.8635C213.963 44.1692 215.632 48.2012 215.632 52.9595C215.632 57.7179 213.978 61.7499 210.657 65.0555C207.305 68.3315 203.179 69.9621 198.278 69.9621C193.348 69.9621 189.236 68.3315 185.944 65.0555C182.667 61.7795 181.014 57.7623 181.014 53.004ZM198.323 62.6837C201.049 62.6837 203.328 61.7647 205.175 59.9117C207.022 58.0143 207.946 55.687 207.916 52.9595C207.856 50.0986 206.918 47.7713 205.086 45.9628C203.239 44.095 200.974 43.1463 198.323 43.1463C195.597 43.1463 193.333 44.0802 191.515 45.9628C189.668 47.8602 188.73 50.2023 188.73 52.9595C188.73 55.7019 189.654 58.0143 191.515 59.9117C193.363 61.7647 195.642 62.6837 198.323 62.6837Z" fill="black"/>
<path d="M230.692 69.4136V43.5466H221.322V36.4461H247.464V43.5466H238.139V69.4136H230.692Z" fill="black"/>
<path d="M268.974 69.9918C265.205 69.9918 262.062 68.9096 259.515 66.7306C256.968 64.5663 255.702 61.32 255.702 57.0063V36.4461H263.15V56.695C263.15 58.711 263.671 60.2527 264.699 61.2903C265.727 62.328 267.157 62.8468 268.974 62.8468C270.732 62.8468 272.147 62.3132 273.189 61.2607C274.232 60.2082 274.753 58.6814 274.753 56.695V36.4461H282.201V57.0212C282.201 61.3052 280.95 64.5515 278.433 66.7454C275.96 68.9096 272.802 69.9918 268.974 69.9918Z" fill="black"/>
<path d="M303.398 69.9918C300.762 69.9918 298.319 69.4729 296.069 68.4501C293.805 67.4273 291.928 65.9449 290.394 64.0179L294.431 58.7555C296.918 61.6758 299.912 63.1285 303.443 63.1285C304.724 63.1285 305.767 62.8468 306.586 62.2984C307.39 61.7499 307.792 61.0235 307.792 60.1341C307.792 58.9186 306.839 57.8958 304.917 57.0508L301.953 55.9835C300.136 55.3016 298.706 54.6939 297.693 54.1602C294.133 52.2332 292.36 49.4463 292.36 45.8146C292.36 42.9536 293.448 40.5967 295.637 38.7141C297.812 36.8463 300.657 35.8976 304.158 35.8976C308.552 35.8976 312.306 37.3503 315.404 40.2706L311.725 45.7553C310.772 44.8659 309.595 44.1544 308.209 43.6207C306.824 43.0871 305.498 42.8202 304.247 42.8202C303.026 42.8202 302.057 43.0722 301.357 43.5614C300.657 44.0506 300.3 44.7176 300.3 45.5478C300.3 46.6744 301.194 47.6379 302.996 48.4532L306.228 49.7428C308.224 50.5136 309.505 51.0325 310.042 51.2993C314.019 53.0781 316 55.8798 316 59.6894C316 62.7282 314.838 65.1889 312.499 67.1012C310.206 69.0282 307.167 69.9918 303.398 69.9918Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_97_2">
<rect width="286" height="63" fill="white" transform="translate(30 21)"/>
</clipPath>
</defs>
</svg>

src/assets/images/connectwithstripe.svg METASEP
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	 viewBox="0 0 180 38" style="enable-background:new 0 0 180 38;" xml:space="preserve">
<style type="text/css">
	.st0{fill:#635BFF;}
	.st1{fill:#FFFFFF;}
</style>
<path class="st0" d="M4,0h172c2.2,0,4,1.8,4,4v30c0,2.2-1.8,4-4,4H4c-2.2,0-4-1.8-4-4V4C0,1.8,1.8,0,4,0z"/>
<path class="st1" d="M26.4,18.6c0-2.3,1.3-3.9,3-3.9c1.3,0,2.2,0.8,2.5,2l1.8-0.6c-0.6-1.9-2.1-3.1-4.3-3.1c-2.9,0-5,2.3-5,5.6
	s2.1,5.6,5,5.6c2.2,0,3.7-1.2,4.3-3.1l-1.8-0.6c-0.3,1.3-1.2,2-2.5,2C27.6,22.5,26.4,20.9,26.4,18.6z M42,20.1
	c0-2.4-1.6-4.1-3.8-4.1s-3.8,1.6-3.8,4.1s1.6,4.1,3.8,4.1C40.5,24.2,42,22.5,42,20.1z M36.3,20.1c0-1.6,0.8-2.6,2-2.6s2,1,2,2.6
	s-0.8,2.6-2,2.6S36.3,21.7,36.3,20.1z M43.4,24h1.8v-4.6c0-1.1,0.8-1.7,1.6-1.7c1,0,1.4,0.7,1.4,1.7V24H50v-5.2c0-1.7-1-2.8-2.6-2.8
	c-1,0-1.7,0.5-2.2,1v-0.8h-1.8V24z M51.9,24h1.8v-4.6c0-1.1,0.8-1.7,1.6-1.7c1,0,1.4,0.7,1.4,1.7V24h1.8v-5.2c0-1.7-1-2.8-2.6-2.8
	c-1,0-1.7,0.5-2.2,1v-0.8h-1.8V24z M63.7,24.2c1.6,0,2.8-0.8,3.4-2.2l-1.5-0.6c-0.2,0.8-0.9,1.3-1.8,1.3c-1.2,0-2-0.8-2.1-2.2h5.5
	v-0.6c0-2.2-1.2-3.9-3.5-3.9c-2.2,0-3.8,1.8-3.8,4.1C59.9,22.5,61.4,24.2,63.7,24.2z M63.6,17.5c1.1,0,1.7,0.8,1.7,1.6h-3.6
	C61.9,18,62.7,17.5,63.6,17.5z M69.8,20.1c0-1.6,0.8-2.5,2.1-2.5c1,0,1.5,0.6,1.8,1.5l1.5-0.8c-0.4-1.3-1.6-2.2-3.3-2.2
	c-2.2,0-3.9,1.7-3.9,4.1s1.6,4.1,3.9,4.1c1.7,0,2.9-1,3.3-2.3l-1.5-0.8c-0.2,0.9-0.8,1.5-1.8,1.5C70.6,22.6,69.8,21.7,69.8,20.1z
	 M76.4,22c0,1.6,0.8,2.1,2.3,2.1c0.5,0,0.9,0,1.2-0.1v-1.5c-0.3,0-0.4,0-0.7,0c-0.6,0-1.1-0.1-1.1-0.8v-4.1h1.7v-1.5h-1.7v-2.3h-1.8
	v2.3h-1.1v1.5h1.1L76.4,22L76.4,22z M88.1,16.1l-1.4,5.2l-1.4-5.2h-1.8l2.4,7.9h1.6l1.4-5.2l1.4,5.2h1.6l2.4-7.9h-1.8L91,21.3
	l-1.4-5.2C89.6,16.1,88.1,16.1,88.1,16.1z M95.2,15.1H97v-1.8h-1.9L95.2,15.1L95.2,15.1z M97,16.1h-1.8V24H97V16.1z M99,22
	c0,1.6,0.8,2.1,2.3,2.1c0.5,0,0.9,0,1.2-0.1v-1.5c-0.3,0-0.4,0-0.7,0c-0.6,0-1.1-0.1-1.1-0.8v-4.1h1.7v-1.5h-1.7v-2.3H99v2.3h-1.1
	v1.5H99V22z M103.9,24h1.8v-4.6c0-1.1,0.8-1.7,1.6-1.7c1,0,1.4,0.7,1.4,1.7V24h1.8v-5.2c0-1.7-1-2.8-2.6-2.8c-1,0-1.7,0.5-2.2,1
	v-3.7h-1.8C103.9,13.3,103.9,24,103.9,24z"/>
<path class="st1" d="M141.5,26.4v-3c0.4,0.3,0.9,0.7,1.9,0.7c1.9,0,3.6-1.5,3.6-4.9c0-3.1-1.7-4.8-3.6-4.8c-1,0-1.7,0.5-2.1,0.9
	l-0.1-0.6h-2.3V27L141.5,26.4z M142.7,16.7c1,0,1.6,1.1,1.6,2.5c0,1.5-0.6,2.5-1.6,2.5c-0.6,0-1-0.2-1.3-0.5v-4
	C141.8,17,142.1,16.7,142.7,16.7z"/>
<path class="st1" d="M152.1,24.2c1.3,0,2.2-0.3,3-0.7v-2.2c-0.7,0.4-1.5,0.6-2.6,0.6s-1.9-0.4-2.1-1.7h5.2c0-0.2,0-0.7,0-1
	c0-2.8-1.3-4.9-3.8-4.9s-4.1,2.2-4.1,4.9C147.7,22.5,149.5,24.2,152.1,24.2z M151.8,16.5c0.6,0,1.3,0.5,1.3,1.7h-2.8
	C150.4,17.1,151.1,16.5,151.8,16.5z"/>
<path class="st1" d="M132,17.5c0.6-0.9,1.7-0.7,2-0.6v-2.5c-0.4-0.1-1.6-0.4-2.1,0.8l-0.2-0.8h-2.3v9.4h2.6V17.5z"/>
<path class="st1" d="M120,21.2c0,0.5-0.4,0.6-1,0.6c-0.9,0-2-0.4-2.9-0.9v2.6c1,0.4,2,0.6,2.9,0.6c2.2,0,3.7-1.1,3.7-3
	c0-3.2-4-2.6-4-3.8c0-0.4,0.3-0.6,0.9-0.6c0.8,0,1.8,0.2,2.6,0.7v-2.6c-0.9-0.4-1.7-0.5-2.6-0.5c-2.1,0-3.6,1.1-3.6,3
	C116,20.4,120,19.9,120,21.2z"/>
<path class="st1" d="M126.4,24.2c0.8,0,1.5-0.2,1.9-0.4v-2.2c-0.3,0.1-2,0.6-2-1v-3.8h2v-2.3h-2v-2.3l-2.6,0.6v8.6
	C123.7,23,124.8,24.2,126.4,24.2z"/>
<path class="st1" d="M134.9,11.6v2.1l2.7-0.5V11L134.9,11.6z"/>
<path class="st1" d="M137.5,14.5h-2.6v9.3h2.6V14.5z"/>
</svg>

src/types/stripe-type.ts METASEP
export interface StripeConnectType {
  code: string;
  scope: string;
  client_id: string;
}

export interface StripeStatusType {
  connected: boolean;
}

export interface StripeOauthType {
  details: string;
  success: boolean;
}

src/types/plan-type.ts METASEP
export interface PlanType {
  name: string;
  components: Component[];
  billing_interval: string;
  description: string;
  flat_rate: number;
}

interface Component {
  metric_name: string;
  property_name: string;
  free_metric_quantity: number;
  cost_per_metric: number;
  aggregation_type: string;
  unit_per_cost: number;
}

export interface PlanDisplay {
  name: string;
  color: string;
}

src/types/customer-type.ts METASEP
import { PlanDisplay } from "./plan-type";
export interface CustomerType {
  name: string;
  billing_id?: string;
  balance?: string;
  customer_id: string;
}
export interface CustomerTableItem extends CustomerType {
  plan: PlanDisplay;
}

src/pages/ViewSettings.tsx METASEP
import React, { FC } from "react";
import { Card, Col, Row } from "antd";
import Settings from "../components/Settings";

const ViewSettings: FC = () => {
  return (
    <div>
      <Settings />
    </div>
  );
};

export default ViewSettings;

src/pages/ViewPlans.tsx METASEP
import React, { FC, useEffect, useState } from "react";
import { Card, Col, Row, List } from "antd";
import { Plan } from "../api/api";
import { PlanType } from "../types/plan-type";

const ViewPlans: FC = () => {
  const [plans, setPlans] = useState<PlanType[]>([]);

  useEffect(() => {
    Plan.getPlans().then((data) => {
      setPlans(data);
    });
  }, []);

  return (
    <div>
      <h1 className="bg-grey1">Plans</h1>
      <br />
      <div className="site-card-wrapper">
        <Row gutter={18}>
          {plans.map((plan, k) => (
            <Col key={k} span={6}>
              <Card title={plan.name} bordered={true}>
                <p>{plan.description}</p>
                <p>Billing Interval: {plan.billing_interval}</p>
                <p>
                  Subscription Rate: ${plan.flat_rate}/{plan.billing_interval}
                </p>
                <List
                  bordered={true}
                  itemLayout="horizontal"
                  dataSource={plan.components}
                  renderItem={(item) => (
                    <List.Item>
                      <List.Item.Meta
                        title={item.metric_name}
                        description={
                          <div>
                            <p>
                              Aggregation type: {item.aggregation_type} over{" "}
                              {item.property_name}
                            </p>
                            <p>
                              Included Initial Amount:
                              <b>{item.free_metric_quantity}</b>
                            </p>
                            <p>
                              Then <b>${item.cost_per_metric}</b> per{" "}
                              <b>
                                {item.unit_per_cost} {item.metric_name}
                              </b>
                            </p>
                          </div>
                        }
                      />
                    </List.Item>
                  )}
                />
              </Card>
            </Col>
          ))}
        </Row>
      </div>
    </div>
  );
};

export default ViewPlans;

src/pages/ViewCustomers.tsx METASEP
import React, { FC, useEffect, useState } from "react";
import CustomerTable from "../components/CustomerTable";
import { CustomerTableItem } from "../types/customer-type";
import { Customer } from "../api/api";
import * as Toast from "@radix-ui/react-toast";

const customer_data = [
  {
    customer_id: "1",
    name: "Leanne Graham",
    plans: 3,
  },
  {
    customer_id: "2",
    name: "Ervin Howell",
    plans: 2,
  },
  { customer_id: "3", name: "Clementine Bauch", plans: 3 },
  { customer_id: "4", name: "Patricia Lebsack", plans: 2 },
];

const ViewCustomers: FC = () => {
  const [customers, setCustomers] = useState<CustomerTableItem[]>([]);

  useEffect(() => {
    Customer.getCustomers().then((data) => {
      setCustomers(data);
    });
  }, []);

  return (
    <div>
      <CustomerTable customerArray={customers} />
    </div>
  );
};

export default ViewCustomers;

src/pages/StripeRedirect.tsx METASEP
import React, { FC, useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { StripeConnect } from "../api/api";
import { StripeOauthType } from "../types/stripe-type";
import { useQuery, UseQueryResult } from "react-query";
import { useSearchParams } from "react-router-dom";
import { useNavigate } from "react-router-dom";

const StripeRedirect: FC = () => {
  let [searchParams, setSearchParams] = useSearchParams();
  const [connected, setConnected] = useState<string | boolean>(
    "Not Yet Connected"
  );
  const navigate = useNavigate();

  const code = searchParams.get("code") || "";

  useEffect(() => {
    if (code !== "") {
      StripeConnect.connectStripe(code)
        .then((data) => {
          setConnected(data.success);
        })
        .catch((error) => {
          setConnected(error.response.data.details);
        });
    }
  }, []);
  if (searchParams.get("error")) {
    return <div>{searchParams.get("error")}</div>;
  }
  const returnToDashboard = () => {
    navigate("/dashboard");
  };

  return (
    <div>
      <h1>Stripe Redirect: {connected} </h1>
      <button onClick={returnToDashboard}>Go To Dashboard</button>
    </div>
  );
};

export default StripeRedirect;

src/pages/Login.tsx METASEP
import React, { FC, useEffect, useState } from "react";
import CustomerTable from "../components/CustomerTable";
import { CustomerType } from "../types/customer-type";
import { Customer } from "../api/api";
import * as Toast from "@radix-ui/react-toast";
import { useNavigate } from "react-router-dom";
import { Authentication } from "../api/api";
import "./Login.css";

interface LoginForm extends HTMLFormControlsCollection {
  username: string;
  password: string;
}

interface FormElements extends HTMLFormElement {
  readonly elements: LoginForm;
}

const Login: FC = () => {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const navigate = useNavigate();

  const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setPassword(event.target.value);
  };

  const handleUserNameChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setUsername(event.target.value);
  };

  const redirectDashboard = () => {
    navigate("/dashboard");
  };

  const handleLogin = (event: React.FormEvent<FormElements>) => {
    event.preventDefault();
    Authentication.login(username, password).then((data) => {
      setIsAuthenticated(true);
    });
  };

  if (!isAuthenticated) {
    return (
      <>
        <div className="container mt-3">
          <img src="../assets/images/logo_large.jpg" alt="logo" />
          <h2>Login</h2>
          <form onSubmit={handleLogin}>
            <div className="form-group">
              <label htmlFor="username">Username</label>
              <input
                type="text"
                className="form-control"
                id="username"
                name="username"
                value={username}
                onChange={handleUserNameChange}
              />
            </div>
            <div className="form-group">
              <label htmlFor="username">Password</label>
              <input
                type="password"
                className="form-control"
                id="password"
                name="password"
                value={password}
                onChange={handlePasswordChange}
              />
              <div>
                {error && <small className="text-danger">{error}</small>}
              </div>
            </div>
            <button type="submit" className="btn btn-primary">
              Login
            </button>
          </form>
        </div>
      </>
    );
  }
  return (
    <div className="container mt-3">
      <h1>Login</h1>
      <p>Hi {username}. You are logged in!</p>
      <button className="btn btn-primary mr-2" onClick={redirectDashboard}>
        Dashboard
      </button>
      <button className="btn btn-danger">Log out</button>
    </div>
  );
};

export default Login;

src/pages/Login.css METASEP
img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

src/config/Routes.tsx METASEP
import React, { FC, useState, useEffect } from "react";
import { Routes, Route, Navigate } from "react-router-dom";
import Dashboard from "../components/Dashboard";
import ViewPlans from "../pages/ViewPlans";
import ViewCustomers from "../pages/ViewCustomers";
import ViewSettings from "../pages/ViewSettings";
import StripeRedirect from "../pages/StripeRedirect";

// import MainContent from "./components/MainContent";
import SideBar from "../components/SideBar";
import { Layout } from "antd";
import { MenuUnfoldOutlined, MenuFoldOutlined } from "@ant-design/icons";

const { Header, Sider, Content, Footer } = Layout;

const AppRoutes: FC = () => {
  const [collapse, setCollapse] = useState(false);

  const handleToggle = (event: any) => {
    event.preventDefault();
    collapse ? setCollapse(false) : setCollapse(true);
  };

  useEffect(() => {
    window.innerWidth <= 760 ? setCollapse(true) : setCollapse(false);
  }, []);
  return (
    <div>
      <Layout>
        <Sider trigger={null} collapsible collapsed={collapse}>
          <SideBar />
        </Sider>
        <Layout>
          <Header
            className="siteLayoutBackground"
            style={{ padding: 0, background: "#001529" }}
          >
            {React.createElement(
              collapse ? MenuUnfoldOutlined : MenuFoldOutlined,
              {
                className: "trigger",
                onClick: handleToggle,
                style: { color: "#fff" },
              }
            )}
          </Header>
          <Content
            style={{
              margin: "24px 16px",
              padding: 24,
              minHeight: "calc(100vh - 114px)",
              background: "#fff",
            }}
          >
            <Routes>
              <Route path="/dashboard" element={<Dashboard />} />
              <Route path="/plans" element={<ViewPlans />} />
              <Route path="/customers" element={<ViewCustomers />} />
              <Route path="/settings" element={<ViewSettings />} />
              <Route path="/redirectstripe" element={<StripeRedirect />} />
            </Routes>
          </Content>
          <Footer style={{ textAlign: "center" }}>Lotus @2022</Footer>
        </Layout>
      </Layout>
    </div>
  );
};

export default AppRoutes;

src/components/SideBar.tsx METASEP
import React, { FC } from "react";
import { Menu } from "antd";
import {
  UserOutlined,
  VideoCameraOutlined,
  UploadOutlined,
} from "@ant-design/icons";
import { useNavigate } from "react-router";
import logo from "../assets/images/corner_logo.svg";
import "./SideBar.css";

const SideBar: FC = () => {
  const navigate = useNavigate();

  const handleCustomersClick = () => {
    navigate("/customers");
  };
  const handlePlansClick = () => {
    navigate("/plans");
  };
  const handleDashboardClick = () => {
    navigate("/dashboard");
  };
  const handleSettingsClick = () => {
    navigate("/settings");
  };

  return (
    <div>
      <div
        style={{
          height: "32px",
          background: "#fff",
          margin: "16px",
        }}
      >
        <img src={logo} alt="lotus" />
      </div>
      <Menu theme="dark" mode="vertical" defaultSelectedKeys={["1"]}>
        <Menu.Item key="1" onClick={handleDashboardClick}>
          <UserOutlined />
          <span> Dashboard</span>
        </Menu.Item>
        <Menu.Item key="2" onClick={handleCustomersClick}>
          <VideoCameraOutlined />
          <span> Customers</span>
        </Menu.Item>
        <Menu.Item key="3" onClick={handlePlansClick}>
          <UploadOutlined />
          <span> Plans</span>
        </Menu.Item>
        <Menu.Item title="Settings" onClick={handleSettingsClick}>
          <span> Settings</span>
        </Menu.Item>
      </Menu>
    </div>
  );
};

export default SideBar;

src/components/SideBar.css METASEP
img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
.landscape {
  height: 32px;
  width: 16px;
}

src/components/Settings.tsx METASEP
import React, { FC, useState } from "react";
import "./Settings.css";
import { StripeStatusType } from "../types/stripe-type";
import { useQuery, UseQueryResult } from "react-query";
import { StripeConnect } from "../api/api";
import axios from "axios";
import { useNavigate } from "react-router-dom";
import { loadEnv } from "vite";

const Settings: FC = () => {
  const navigate = useNavigate();
  const [connectedStatus, setConnectedStatus] = useState<boolean>(false);

  const fetchStripeConnect = async (): Promise<StripeStatusType> =>
    StripeConnect.getStripeConnectionStatus().then((data) => {
      return data;
    });

  const { status, error, data } = useQuery<StripeStatusType>(
    ["stripeConnect"],
    fetchStripeConnect
  );

  const handleConnectWithStripeClick = () => {
    const client_id: string = import.meta.env.VITE_STRIPE_CLIENT;
    const query = new URLSearchParams({
      response_type: "code",
      client_id: import.meta.env.VITE_STRIPE_CLIENT,
      scope: "read_write",
      redirect_uri: import.meta.env.VITE_API_URL + "redirectstripe",
    });
    let path: string = "https://connect.stripe.com/oauth/authorize?" + query;
    window.location.href = path;
  };
  return (
    <div>
      <h1>Settings</h1>
      <div>
        {data?.connected === true ? (
          <div>
            <p>Connected to Stripe &#x2705;</p>{" "}
          </div>
        ) : (
          <div>
            <p>Not Connected to Stripe </p>{" "}
            <a
              className="stripe-connect slate"
              onClick={handleConnectWithStripeClick}
            >
              <span>Connect with</span>
            </a>
          </div>
        )}
      </div>
    </div>
  );
};

export default Settings;

src/components/Settings.css METASEP
body {
  margin: 0%;
}

.stripe-connect {
  background: #635bff;
  display: inline-block;
  height: 38px;
  text-decoration: none;
  width: 180px;

  border-radius: 4px;
  -moz-border-radius: 4px;
  -webkit-border-radius: 4px;

  user-select: none;
  -moz-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;

  -webkit-font-smoothing: antialiased;
}

.stripe-connect span {
  color: #ffffff;
  display: block;
  font-family: sohne-var, "Helvetica Neue", Arial, sans-serif;
  font-size: 0.9375rem;
  font-weight: 400;
  line-height: 14px;
  padding: 11px 0px 0px 24px;
  position: relative;
  text-align: left;
}

.stripe-connect:hover {
  background: #7a73ff;
}

.stripe-connect.slate {
  background: #0a2540;
}

.stripe-connect.slate:hover {
  background: #425466;
}

.stripe-connect.white {
  background: #ffffff;
  left: 1px;
  right: 1px;
  position: relative;
}

.stripe-connect.white span {
  color: #0a2540;
}

.stripe-connect.white:hover {
  background: #f6f9fc;
}

.stripe-connect span::after {
  background-repeat: no-repeat;
  background-size: 49.58px;
  content: "";
  height: 20px;
  left: 62%;
  position: absolute;
  top: 28.95%;
  width: 49.58px;
}

/* Logos */
.stripe-connect span::after {
  background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 23.0.4, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 468 222.5' style='enable-background:new 0 0 468 222.5;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill-rule:evenodd;clip-rule:evenodd;fill:%23FFFFFF;%7D%0A%3C/style%3E%3Cg%3E%3Cpath class='st0' d='M414,113.4c0-25.6-12.4-45.8-36.1-45.8c-23.8,0-38.2,20.2-38.2,45.6c0,30.1,17,45.3,41.4,45.3 c11.9,0,20.9-2.7,27.7-6.5v-20c-6.8,3.4-14.6,5.5-24.5,5.5c-9.7,0-18.3-3.4-19.4-15.2h48.9C413.8,121,414,115.8,414,113.4z M364.6,103.9c0-11.3,6.9-16,13.2-16c6.1,0,12.6,4.7,12.6,16H364.6z'/%3E%3Cpath class='st0' d='M301.1,67.6c-9.8,0-16.1,4.6-19.6,7.8l-1.3-6.2h-22v116.6l25-5.3l0.1-28.3c3.6,2.6,8.9,6.3,17.7,6.3 c17.9,0,34.2-14.4,34.2-46.1C335.1,83.4,318.6,67.6,301.1,67.6z M295.1,136.5c-5.9,0-9.4-2.1-11.8-4.7l-0.1-37.1 c2.6-2.9,6.2-4.9,11.9-4.9c9.1,0,15.4,10.2,15.4,23.3C310.5,126.5,304.3,136.5,295.1,136.5z'/%3E%3Cpolygon class='st0' points='223.8,61.7 248.9,56.3 248.9,36 223.8,41.3 '/%3E%3Crect x='223.8' y='69.3' class='st0' width='25.1' height='87.5'/%3E%3Cpath class='st0' d='M196.9,76.7l-1.6-7.4h-21.6v87.5h25V97.5c5.9-7.7,15.9-6.3,19-5.2v-23C214.5,68.1,202.8,65.9,196.9,76.7z'/%3E%3Cpath class='st0' d='M146.9,47.6l-24.4,5.2l-0.1,80.1c0,14.8,11.1,25.7,25.9,25.7c8.2,0,14.2-1.5,17.5-3.3V135 c-3.2,1.3-19,5.9-19-8.9V90.6h19V69.3h-19L146.9,47.6z'/%3E%3Cpath class='st0' d='M79.3,94.7c0-3.9,3.2-5.4,8.5-5.4c7.6,0,17.2,2.3,24.8,6.4V72.2c-8.3-3.3-16.5-4.6-24.8-4.6 C67.5,67.6,54,78.2,54,95.9c0,27.6,38,23.2,38,35.1c0,4.6-4,6.1-9.6,6.1c-8.3,0-18.9-3.4-27.3-8v23.8c9.3,4,18.7,5.7,27.3,5.7 c20.8,0,35.1-10.3,35.1-28.2C117.4,100.6,79.3,105.9,79.3,94.7z'/%3E%3C/g%3E%3C/svg%3E");
}

.stripe-connect.white span::after {
  background-image: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) --%3E%3Csvg version='1.1' id='Layer_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' viewBox='0 0 468 222.5' style='enable-background:new 0 0 468 222.5;' xml:space='preserve'%3E%3Cstyle type='text/css'%3E .st0%7Bfill-rule:evenodd;clip-rule:evenodd;fill:%230A2540;%7D%0A%3C/style%3E%3Cg%3E%3Cpath class='st0' d='M414,113.4c0-25.6-12.4-45.8-36.1-45.8c-23.8,0-38.2,20.2-38.2,45.6c0,30.1,17,45.3,41.4,45.3 c11.9,0,20.9-2.7,27.7-6.5v-20c-6.8,3.4-14.6,5.5-24.5,5.5c-9.7,0-18.3-3.4-19.4-15.2h48.9C413.8,121,414,115.8,414,113.4z M364.6,103.9c0-11.3,6.9-16,13.2-16c6.1,0,12.6,4.7,12.6,16H364.6z'/%3E%3Cpath class='st0' d='M301.1,67.6c-9.8,0-16.1,4.6-19.6,7.8l-1.3-6.2h-22v116.6l25-5.3l0.1-28.3c3.6,2.6,8.9,6.3,17.7,6.3 c17.9,0,34.2-14.4,34.2-46.1C335.1,83.4,318.6,67.6,301.1,67.6z M295.1,136.5c-5.9,0-9.4-2.1-11.8-4.7l-0.1-37.1 c2.6-2.9,6.2-4.9,11.9-4.9c9.1,0,15.4,10.2,15.4,23.3C310.5,126.5,304.3,136.5,295.1,136.5z'/%3E%3Cpolygon class='st0' points='223.8,61.7 248.9,56.3 248.9,36 223.8,41.3 '/%3E%3Crect x='223.8' y='69.3' class='st0' width='25.1' height='87.5'/%3E%3Cpath class='st0' d='M196.9,76.7l-1.6-7.4h-21.6v87.5h25V97.5c5.9-7.7,15.9-6.3,19-5.2v-23C214.5,68.1,202.8,65.9,196.9,76.7z'/%3E%3Cpath class='st0' d='M146.9,47.6l-24.4,5.2l-0.1,80.1c0,14.8,11.1,25.7,25.9,25.7c8.2,0,14.2-1.5,17.5-3.3V135 c-3.2,1.3-19,5.9-19-8.9V90.6h19V69.3h-19L146.9,47.6z'/%3E%3Cpath class='st0' d='M79.3,94.7c0-3.9,3.2-5.4,8.5-5.4c7.6,0,17.2,2.3,24.8,6.4V72.2c-8.3-3.3-16.5-4.6-24.8-4.6 C67.5,67.6,54,78.2,54,95.9c0,27.6,38,23.2,38,35.1c0,4.6-4,6.1-9.6,6.1c-8.3,0-18.9-3.4-27.3-8v23.8c9.3,4,18.7,5.7,27.3,5.7 c20.8,0,35.1-10.3,35.1-28.2C117.4,100.6,79.3,105.9,79.3,94.7z'/%3E%3C/g%3E%3C/svg%3E");
}

src/components/Dashboard.tsx METASEP
import React, { FC } from "react";
import logo_large from "../assets/images/logo_large.jpg";

const Dashboard: FC = () => {
  return (
    <div>
      <h1 className="bg-grey1">Dashboard</h1>
      <img src={logo_large} alt="lotus" height="500" width="auto" />
    </div>
  );
};

export default Dashboard;

src/components/CustomerTable.tsx METASEP
import React, { FC, useState, useEffect } from "react";
import {
  DownOutlined,
  EllipsisOutlined,
  QuestionCircleOutlined,
} from "@ant-design/icons";
import type { ProColumns } from "@ant-design/pro-components";
import { ProTable } from "@ant-design/pro-components";
import { Table } from "antd";
import { CustomerTableItem } from "../types/customer-type";
import { Button, Tag, Tooltip } from "antd";
import { PlanDisplay } from "../types/plan-type";
import { useNavigate } from "react-router-dom";

const columns: ProColumns<CustomerTableItem>[] = [
  {
    title: "id",
    width: 120,
    dataIndex: "customer_id",
    align: "left",
  },
  {
    title: "name",
    width: 120,
    dataIndex: "name",
    align: "left",
  },
  {
    title: "plan",
    width: 120,
    dataIndex: "plan.name",
    render: (_, record) => (
      <Tag color={record.plan.color}>{record.plan.name}</Tag>
    ),
  },
];

interface Props {
  customerArray: CustomerTableItem[];
}

const ViewCustomers: FC<Props> = ({ customerArray }) => {
  const navigate = useNavigate();

  const navigateCreateCustomer = () => {
    navigate("/customers/create");
  };
  return (
    <ProTable<CustomerTableItem>
      columns={columns}
      dataSource={customerArray}
      rowKey="customer_id"
      search={false}
      pagination={{
        showTotal: (total, range) => (
          <div>{`${range[0]}-${range[1]} of ${total} total items`}</div>
        ),
      }}
      headerTitle="Customer Table"
      options={false}
      toolBarRender={() => [
        <Button key="primary" type="primary" onClick={navigateCreateCustomer}>
          Create Customer
        </Button>,
      ]}
    />
  );
};

export default ViewCustomers;

src/api/api.ts METASEP
import axios, { AxiosResponse } from "axios";
import { CustomerTableItem, CustomerType } from "../types/customer-type";
import { PlanType } from "../types/plan-type";
import {
  StripeConnectType,
  StripeOauthType,
  StripeStatusType,
} from "../types/stripe-type";
import Cookies from "universal-cookie";

const cookies = new Cookies();

axios.defaults.headers.common["X-CSRFToken"] = cookies.get("csrftoken");

const instance = axios.create({
  timeout: 15000,
  withCredentials: true,
});

const responseBody = (response: AxiosResponse) => response.data;

const requests = {
  get: (url: string) => instance.get(url).then(responseBody),
  post: (url: string, body: {}, headers?: {}) =>
    instance.post(url, body, headers).then(responseBody),
  put: (url: string, body: {}) => instance.put(url, body).then(responseBody),
  delete: (url: string) => instance.delete(url).then(responseBody),
};

export const Customer = {
  getCustomers: (): Promise<CustomerTableItem[]> =>
    requests.get("api/customers"),
  getACustomer: (id: number): Promise<CustomerType> =>
    requests.get(`api/customers/${id}`),
  createCustomer: (post: CustomerType): Promise<CustomerType> =>
    requests.post("api/customers", post),
};

export const Plan = {
  getPlans: (): Promise<PlanType[]> => requests.get("api/plans"),
};

export const StripeConnect = {
  getStripeConnectionStatus: (): Promise<StripeStatusType> =>
    requests.get("api/stripe"),
  connectStripe: (authorization_code: string): Promise<StripeOauthType> =>
    requests.post("api/stripe", { authorization_code }),
};

export const Authentication = {
  getSession: (): Promise<{ isAuthenticated: boolean }> =>
    requests.get("api/session/"),
  login: (
    username: string,
    password: string
  ): Promise<{ username: string; password: string }> =>
    requests.post("api/login/", { username, password }),
};

src/vite-env.d.ts METASEP
/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string;
  // more env variables...
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

src/main.tsx METASEP
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import { BrowserRouter } from "react-router-dom";
import "vite/modulepreload-polyfill";
import { QueryClient, QueryClientProvider } from "react-query";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      refetchOnWindowFocus: false,
      refetchOnReconnect: false,
      retry: false,
      staleTime: 5 * 60 * 1000,
    },
  },
});
const root = ReactDOM.createRoot(
  document.getElementById("root") as HTMLElement
);
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <QueryClientProvider client={queryClient}>
        <App />
      </QueryClientProvider>
    </BrowserRouter>
  </React.StrictMode>
);

src/index.html METASEP
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link
      rel="icon"
      type="image/svg+xml"
      href="./assets/images/logo_small.svg"
    />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Lotus Application</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="main.tsx"></script>
  </body>
</html>

src/index.css METASEP
@import url("https://rsms.me/inter/inter.css");

html,
body {
  padding: 0;
  margin: 0;
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
  @apply font-inter;
}

src/Data.tsx METASEP
export const customer_data = [
  {
    customer_id: 1,
    name: "Leanne Graham",
    plans: [],
  },
  {
    customer_id: 2,
    name: "Ervin Howell",
    plans: [],
  },
  { customer_id: 3, name: "Clementine Bauch", plans: [] },
  { customer_id: 4, name: "Patricia Lebsack", plans: [] },
];

src/App.tsx METASEP
import React from "react";
import AppRoutes from "./config/Routes";
import "./App.css";
import { Authentication } from "./api/api";
import { useQuery, UseQueryResult } from "react-query";
import Login from "./pages/Login";

function App() {
  const fetchSessionInfo = async (): Promise<{ isAuthenticated: boolean }> =>
    Authentication.getSession().then((res) => {
      return res;
    });

  const { data: sessionData, isLoading } = useQuery<{
    isAuthenticated: boolean;
  }>(["session"], fetchSessionInfo);

  const isAuthenticated = isLoading ? false : sessionData?.isAuthenticated;
  if (isLoading) {
    return <div>Loading...</div>;
  } else {
    if (isAuthenticated) {
      return <AppRoutes />;
    } else {
      return <Login />;
    }
  }
}

export default App;

src/App.css METASEP
@import "antd/dist/antd.css";
@import "@ant-design/pro-components/dist/components.css";

src/.env.prod METASEP
NODE_ENV=production
VITE_APP_NAME=Lotus-Frontend
VITE_API_URL="https://uselotus.app/"
VITE_STRIPE_CLIENT=123

public/vite.svg METASEP
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
public/logo_small256.jpg METASEP

public/logo_large.png METASEP

design_resources/lotus_ui_home.png METASEP

design_resources/Lotus-Horizontal-Logo-RGB-Black-Medium.svg METASEP
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.2, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.2" baseProfile="tiny" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
	 x="0px" y="0px" viewBox="0 0 960 212" overflow="visible" xml:space="preserve">
<g>
	<g>
		<g>
			<path d="M155.3,145.6C87,138,31.2,88.5,14.5,23.4C13,17.7,11.8,12,11,6.1c-0.3-2-0.6-4.1-0.8-6.1c0.7,0.3,1.5,0.6,2.2,1
				c2.1,0.9,4.2,1.9,6.2,2.8C80,32.4,128.9,83.1,155.3,145.6z"/>
			<path d="M221.4,150.4c-38.4,20.6-82.4,32.2-129,32.2c-5.8,0-11.6-0.2-17.3-0.5c-21.3-15-38.8-35-50.9-58.3c0,0,0,0,0,0
				c-6.3-13.8-11.4-28.3-15.3-43.2C3.1,58.3,0,35.1,0,11c0-2.7,0-5.3,0.1-8c1.2,10.2,3.3,20.1,6.2,29.7c1.8,6,3.9,11.9,6.3,17.6
				c24.8,58.6,80.7,100.8,147,106.1c4.6,0.4,9.3,0.6,14,0.6c4.7,0,9.4-0.2,14.1-0.6C199.3,155.5,210.6,153.5,221.4,150.4z"/>
			<path d="M347,11c0,9.1-0.4,18-1.3,26.9c0,0,0,0,0,0c-1.4,14.7-4,29.1-7.7,43.1c0,0,0,0,0,0.1c-3.9,14.7-8.9,28.9-15.1,42.5
				c-23.4,45-67,77.9-118.9,86.4c0,0-0.1,0-0.1,0c-6.1,0.7-12.2,1.1-18.5,1.4c-3.9,0.2-7.8,0.3-11.7,0.3h-0.3
				c-4.6,0-9.2-0.1-13.7-0.4c-5.3-0.3-10.6-0.7-15.8-1.2c-18.2-2.9-35.4-8.8-51.1-17.1c82-0.1,155.9-35,207.7-90.6
				c0.1-0.1,0.2-0.2,0.2-0.3c1.4-1.5,2.7-2.9,4.1-4.5C317,83.9,327,68.1,334.3,51c2.4-5.7,4.6-11.5,6.4-17.5
				c2-6.5,3.6-13.1,4.8-19.9c0.5-3,1-6,1.4-9.1c0,1.8,0.1,3.6,0.1,5.5C347,10.3,347,10.7,347,11z"/>
			<path d="M337,0c-0.2,2.3-0.5,4.5-0.9,6.8c-0.9,5.9-2.1,11.6-3.5,17.3c-6.6,25.3-19.1,48.2-36,67.2c-0.8,0.9-1.5,1.7-2.3,2.5
				c-26.1,28.3-62.1,47.4-102.4,51.9C218.4,83.2,267.2,32.5,328.4,3.8c2-1,4.1-1.9,6.2-2.8C335.4,0.7,336.2,0.3,337,0z"/>
		</g>
		<ellipse cx="173.8" cy="89.2" rx="28.6" ry="47.3"/>
	</g>
	<g>
		<path d="M488.4,162.7h-80.4V51.5h25V139h55.4V162.7z"/>
		<path d="M506.9,107.4c0-16.2,5.6-29.8,16.7-40.9c11.2-11.2,25-16.9,41.4-16.9c16.3,0,30.1,5.6,41.2,16.9
			c11.2,11.1,16.9,24.7,16.9,40.8c0,16.1-5.6,29.6-16.7,40.8c-11.2,11-25.1,16.6-41.5,16.6c-16.6,0-30.3-5.5-41.4-16.6
			C512.4,136.9,506.9,123.4,506.9,107.4z M565,140c9.1,0,16.8-3.1,23-9.3c6.2-6.4,9.3-14.2,9.2-23.5c-0.2-9.6-3.4-17.5-9.5-23.6
			c-6.2-6.3-13.8-9.5-22.7-9.5c-9.1,0-16.8,3.2-22.9,9.5c-6.2,6.4-9.3,14.3-9.3,23.6c0,9.2,3.1,17.1,9.3,23.5
			C548.4,136.9,556,140,565,140z"/>
		<path d="M673.7,162.7V75.5h-31.4V51.5h87.7v23.9h-31.3v87.3H673.7z"/>
		<path d="M802.2,164.7c-12.6,0-23.2-3.7-31.7-11c-8.5-7.3-12.8-18.3-12.8-32.8V51.5h25v68.3c0,6.8,1.7,12,5.2,15.5
			c3.5,3.5,8.2,5.3,14.4,5.3c5.9,0,10.6-1.8,14.1-5.3c3.5-3.6,5.3-8.7,5.3-15.4V51.5h25v69.4c0,14.4-4.2,25.4-12.6,32.8
			C825.6,161,815,164.7,802.2,164.7z"/>
		<path d="M917.7,164.7c-8.8,0-17-1.7-24.6-5.2c-7.6-3.5-13.9-8.4-19-15l13.5-17.8c8.3,9.8,18.4,14.7,30.2,14.7
			c4.3,0,7.8-0.9,10.5-2.8c2.7-1.9,4.1-4.3,4.1-7.3c0-4.1-3.2-7.6-9.6-10.4l-9.9-3.6c-6.1-2.3-10.9-4.4-14.3-6.2
			c-11.9-6.5-17.9-15.9-17.9-28.1c0-9.6,3.7-17.6,11-23.9c7.3-6.3,16.9-9.5,28.6-9.5c14.7,0,27.3,4.9,37.8,14.7L945.7,83
			c-3.2-3-7.1-5.4-11.8-7.2c-4.7-1.8-9.1-2.7-13.3-2.7c-4.1,0-7.3,0.8-9.7,2.5c-2.4,1.7-3.5,3.9-3.5,6.7c0,3.8,3,7.1,9,9.8l10.8,4.4
			c6.7,2.6,11,4.4,12.8,5.3c13.3,6,20,15.4,20,28.3c0,10.2-3.9,18.6-11.7,25C940.5,161.4,930.4,164.7,917.7,164.7z"/>
	</g>
</g>
</svg>

design_resources/Lotus-Horizontal-Logo-RGB-Black-Medium.jpg METASEP

metering_billing/management/commands/wait_for_db.py METASEP
import time

from django.core.management.base import BaseCommand
from django.db import connections
from django.db.utils import OperationalError


class Command(BaseCommand):
    "Django command to pause execution until the database is available"

    def handle(self, *args, **options):
        "Pause execution until the database is available"

        db_connection = None
        while not db_connection:
            try:
                db_connection = connections["default"]
            except OperationalError:
                time.sleep(1)
        print("Database is available")

metering_billing/management/commands/setup_tasks.py METASEP
import os

from django.core.management import call_command
from django.core.management.base import BaseCommand
from dotenv import load_dotenv
from django_celery_beat.models import PeriodicTask, CrontabSchedule

from metering_billing.models import APIToken, Organization, User

load_dotenv()


class Command(BaseCommand):
    def handle(self, *args, **options):

        schedule, created = CrontabSchedule.objects.get_or_create(
            minute="0", hour="0", day_of_week="*", day_of_month="*", month_of_year="*"
        )
        if created:
            task = PeriodicTask.objects.create(
                name="Check end of subscriptions",
                task="metering_billing.tasks.calculate_invoice",
                crontab=schedule,
            )
            task.save()

metering_billing/management/commands/initadmin.py METASEP
import os

from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.db import connection, connections
from django.db.utils import OperationalError
from dotenv import load_dotenv

from metering_billing.models import APIToken, Organization, User

load_dotenv()


class Command(BaseCommand):
    def handle(self, *args, **options):

        username = os.getenv("DJANGO_SUPERUSER_USERNAME")
        email = os.getenv("DJANGO_SUPERUSER_EMAIL")
        password = os.getenv("DJANGO_SUPERUSER_PASSWORD")

        if not User.objects.filter(username=username).exists():
            admin = User.objects.create_superuser(
                email=email, username=username, password=password, company_name="Lotus"
            )

            org = Organization.objects.create(company_name="Lotus Default")
            org.save()

            org.users.add(admin)
        else:
            print("Admin account has already been initialized.")

metering_billing/management/commands/__init__.py METASEP

lotus/wsgi.py METASEP
"""
WSGI config for lotus project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
"""

import os

from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lotus.settings")

application = get_wsgi_application()

lotus/urls.py METASEP
"""lotus URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/4.0/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from metering_billing import auth_views
from django.urls import path, re_path
from django.conf.urls import include
from django.shortcuts import render
from django.views.generic import TemplateView
from metering_billing import track
from metering_billing.views import (
    CustomerView,
    EventViewSet,
    InitializeStripeView,
    PlansView,
    SubscriptionView,
    SubscriptionViewSet,
    UsageView,
)
from rest_framework import routers

router = routers.DefaultRouter()
router.register(r"event", EventViewSet)


def index(request):
    return render(request, "index.html")


urlpatterns = [
    # path("grappelli/", include("grappelli.urls")),
    path("admin/", admin.site.urls),
    path("api/", include(router.urls)),
    path("api/customers", CustomerView.as_view(), name="customer"),
    path("api/subscriptions", SubscriptionView.as_view(), name="subscription"),
    path("track", track.track_event, name="track_event"),
    path("api/usage", UsageView.as_view(), name="usage"),
    path("api/stripe", InitializeStripeView.as_view(), name="stripe_initialize"),
    path("api/plans", PlansView.as_view(), name="plans"),
    path("api/login/", auth_views.login_view, name="api-login"),
    path("api/logout", auth_views.logout_view, name="api-logout"),
    path("api/session/", auth_views.session_view, name="api-session"),
    path("api/whoami", auth_views.whoami_view, name="api-whoami"),
    re_path(".*", TemplateView.as_view(template_name="index.html")),
]

lotus/settings.py METASEP
"""
Django settings for lotus project.

Generated by 'django-admin startproject' using Django 4.0.6.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""
import os
import re
from pathlib import Path

import dj_database_url
import django_heroku
import environ
import sentry_sdk
from dotenv import load_dotenv
from sentry_sdk.integrations.django import DjangoIntegration

env = environ.Env(
    # set casting, default value
    DEBUG=(bool, False)
)

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
environ.Env.read_env(os.path.join(BASE_DIR, "env/.env"))


try:
    sentry_sdk.init(
        dsn=os.environ["SENTRY_DSN"],
        integrations=[
            DjangoIntegration(),
        ],
        # Set traces_sample_rate to 1.0 to capture 100%
        # of transactions for performance monitoring.
        # We recommend adjusting this value in production.
        traces_sample_rate=1.0,
        # If you wish to associate users to errors (assuming you are using
        # django.contrib.auth) you may enable sending PII data.
        send_default_pii=True,
    )
except KeyError:
    pass


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env("SECRET_KEY")


# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env("DEBUG")

if DEBUG:
    ALLOWED_HOSTS = ["*"]
else:
    ALLOWED_HOSTS = [
        "*uselotus.app",
        "www.uselotus.app",
        "uselotus.app",
        ".herokuapp.com",
    ]

# Application definition

INSTALLED_APPS = [
    # "grappelli",
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    "rest_framework",
    "metering_billing",
    "djmoney",
    "django_extensions",
    "django_celery_beat",
    "rest_framework_api_key",
    "django_vite",
]

MIDDLEWARE = [
    "django.middleware.security.SecurityMiddleware",
    "whitenoise.middleware.WhiteNoiseMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
]

ROOT_URLCONF = "lotus.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [BASE_DIR / "templates"],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
            ],
            "libraries": {
                "render_vite_bundle": "metering_billing.template_tags.render_vite_bundle",
            },
        },
    },
]

WSGI_APPLICATION = "lotus.wsgi.application"

AUTH_USER_MODEL = "metering_billing.User"
# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

if os.environ.get("GITHUB_WORKFLOW"):
    DATABASES = {
        "default": {
            "ENGINE": "django.db.backends.postgresql",
            "NAME": "postgres",
            "USER": "postgres",
            "PASSWORD": "postgres",
            "HOST": "127.0.0.1",
            "PORT": "5432",
        }
    }
else:
    try:
        DATABASES = {
            "default": dj_database_url.parse(
                os.environ["DATABASE_URL"],
                engine="django.db.backends.postgresql",
                conn_max_age=600,
            )
        }
        django_heroku.settings(locals(), databases=False)
    except:
        DATABASES = {
            "default": {
                "ENGINE": "django.db.backends.postgresql",
                "NAME": os.environ["POSTGRES_NAME"],
                "USER": os.environ["POSTGRES_USER"],
                "PASSWORD": os.environ["POSTGRES_PASSWORD"],
                "HOST": os.environ["POSTGRES_HOST"],
                "PORT": 5432,
            }
        }

# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
    },
    {
        "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
    },
]

# Stripe Settings
try:
    STRIPE_SECRET_KEY = os.environ["STRIPE_SECRET_KEY"]
except KeyError:
    STRIPE_SECRET_KEY = ""

# Celery Settings
CELERY_BROKER_URL = os.environ["CELERY_BROKER_URL"]
CELERY_RESULT_BACKEND = os.environ["CELERY_RESULT_BACKEND"]
CELERY_ACCEPT_CONTENT = ["application/json"]
CELERY_TASK_SERIALIZER = "json"
CELERY_RESULT_SERIALIZER = "json"
CELERY_TIMEZONE = "America/New_York"

REDIS_HOST = "localhost"
REDIS_PORT = 6379

# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = "en-us"

TIME_ZONE = "UTC"

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/


STATIC_URL = "/static/"
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
STATICFILES_FINDERS = [
    "django.contrib.staticfiles.finders.FileSystemFinder",
    "django.contrib.staticfiles.finders.AppDirectoriesFinder",
]

INTERNAL_IPS = ["127.0.0.1"]

DJANGO_VITE_DEV_MODE = os.environ["DEBUG"]
DJANGO_VITE_DEV_SERVER_HOST = "localhost"
DJANGO_VITE_DEV_SERVER_PORT = 3000

VITE_APP_DIR = BASE_DIR / "src"

DJANGO_VITE_ASSETS_PATH = BASE_DIR / "static" / "dist"

STATICFILES_DIRS = [DJANGO_VITE_ASSETS_PATH]

STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")


MEDIA_URL = "/mediafiles/"
MEDIA_ROOT = os.path.join(BASE_DIR, "mediafiles")
REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": [
        "metering_billing.permissions.HasUserAPIKey",
    ]
}


# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

CSRF_COOKIE_SAMESITE = "Strict"
SESSION_COOKIE_SAMESITE = "Strict"
CSRF_COOKIE_HTTPONLY = False
SESSION_COOKIE_HTTPONLY = True

# Heroku
django_heroku.settings(locals())

# Vite generates files with 8 hash digits
# http://whitenoise.evans.io/en/stable/django.html#WHITENOISE_IMMUTABLE_FILE_TEST


def immutable_file_test(path, url):
    # Match filename with 12 hex digits before the extension
    # e.g. app.db8f2edc0c8a.js
    return re.match(r"^.+\.[0-9a-f]{8,12}\..+$", url)


WHITENOISE_IMMUTABLE_FILE_TEST = immutable_file_test

lotus/middleware.py METASEP
from ..metering_billing.permissions import HasUserAPIKey
from django.core.exceptions import ObjectDoesNotExist
from rest_framework.authentication import BaseAuthentication
from metering_billing.models import Organization


### NOT YET READY TO BE ADDED INTO AUTHENTICATION
class ApiKeyAuthentication(BaseAuthentication):
    """
    An authentication plugin that authenticates requests through a API key provided in a request header.

    """

    def authenticate(self, request):
        validator = HasUserAPIKey()
        key = validator.get_key(request)

        if not key:
            return None

        try:
            organization_api_key = validator.model.objects.get_from_key(key)
        except ObjectDoesNotExist:
            return None

        request_ip = request.META.get("REMOTE_ADDR")

        if request_ip != organization_api_key.ip:
            return None

        try:
            organization = Organization.objects.get(pk=organization_api_key.user_id)
        except Organization.DoesNotExist:
            return None

        return organization, None

    def authenticate_header(self, request):
        return 'Api-Key realm="api"'

lotus/celery.py METASEP
import os

# Set the default Django settings module for the 'celery' program.
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lotus.settings")
from celery import Celery
from django.conf import settings

app = Celery()

# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
#   should have a `CELERY_` prefix.
# app.config_from_object("django.conf:settings", namespace="CELERY")


# Load task modules from all registered Django apps.
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)


@app.task(bind=True, ignore_result=True)
def debug_task(self):
    print(f"Request: {self.request!r}")

lotus/asgi.py METASEP
"""
ASGI config for lotus project.

It exposes the ASGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/howto/deployment/asgi/
"""

import os

from django.core.asgi import get_asgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lotus.settings")

application = get_asgi_application()

lotus/__init__.py METASEP
from __future__ import absolute_import, unicode_literals

from .celery import app as celery_app

__all__ = ("celery_app",)

metering_billing/templates/index.html METASEP
{% load django_vite %}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Lotus</title>
    <script type="module">
      import RefreshRuntime from "http://localhost:3000/@react-refresh";
      if (RefreshRuntime) {
        RefreshRuntime.injectIntoGlobalHook(window);
        window.$RefreshReg$ = () => {};
        window.$RefreshSig$ = () => (type) => type;
        window.__vite_plugin_react_preamble_installed__ = true;
      }
    </script>
    <div id="root"></div>
    {% vite_hmr_client %} {% vite_asset 'main.tsx' %}
  </head>
  <body></body>
</html>

yarn.lock METASEP
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@ampproject/remapping@^2.1.0":
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
  integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
  dependencies:
    "@jridgewell/gen-mapping" "^0.1.0"
    "@jridgewell/trace-mapping" "^0.3.9"

"@ant-design/colors@^6.0.0":
  version "6.0.0"
  resolved "https://registry.yarnpkg.com/@ant-design/colors/-/colors-6.0.0.tgz#9b9366257cffcc47db42b9d0203bb592c13c0298"
  integrity sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==
  dependencies:
    "@ctrl/tinycolor" "^3.4.0"

"@ant-design/icons-svg@^4.2.1":
  version "4.2.1"
  resolved "https://registry.yarnpkg.com/@ant-design/icons-svg/-/icons-svg-4.2.1.tgz#8630da8eb4471a4aabdaed7d1ff6a97dcb2cf05a"
  integrity sha512-EB0iwlKDGpG93hW8f85CTJTs4SvMX7tt5ceupvhALp1IF44SeUFOMhKUOYqpsoYWQKAOuTRDMqn75rEaKDp0Xw==

"@ant-design/icons@^4.0.0", "@ant-design/icons@^4.1.0", "@ant-design/icons@^4.2.1", "@ant-design/icons@^4.3.0", "@ant-design/icons@^4.7.0":
  version "4.7.0"
  resolved "https://registry.yarnpkg.com/@ant-design/icons/-/icons-4.7.0.tgz#8c3cbe0a556ba92af5dc7d1e70c0b25b5179af0f"
  integrity sha512-aoB4Z7JA431rt6d4u+8xcNPPCrdufSRMUOpxa1ab6mz1JCQZOEVolj2WVs/tDFmN62zzK30mNelEsprLYsSF3g==
  dependencies:
    "@ant-design/colors" "^6.0.0"
    "@ant-design/icons-svg" "^4.2.1"
    "@babel/runtime" "^7.11.2"
    classnames "^2.2.6"
    rc-util "^5.9.4"

"@ant-design/pro-card@1.20.17":
  version "1.20.17"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-card/-/pro-card-1.20.17.tgz#750b92e61659729f6f1afd9ed305e648c2b38333"
  integrity sha512-XKMWBqLC6ZoF53VF9tAhdT4JlRG8inURyB2UXkoqPCiuX6od1rYJvaKerAXF11Bh71HXpPYfNptGdFQCDDSlXQ==
  dependencies:
    "@ant-design/icons" "^4.2.1"
    "@ant-design/pro-utils" "1.44.0"
    "@babel/runtime" "^7.18.0"
    antd "^4.20.0 "
    classnames "^2.2.6"
    omit.js "^2.0.2"
    rc-util "^5.4.0"

"@ant-design/pro-components@^1.1.16":
  version "1.1.16"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-components/-/pro-components-1.1.16.tgz#3439207fa2cf05580dd355828d346e2ce84ef78c"
  integrity sha512-VB1ta13Lcz0JCJzba495HGzswkGWtjGXh/nhjglRaJP3+EIxE3lEthLRWRQPfpa0R2XPrrmp34StY5rCALNlFQ==
  dependencies:
    "@ant-design/pro-card" "1.20.17"
    "@ant-design/pro-descriptions" "1.11.16"
    "@ant-design/pro-field" "1.35.3"
    "@ant-design/pro-form" "1.73.0"
    "@ant-design/pro-layout" "6.38.18"
    "@ant-design/pro-list" "1.21.78"
    "@ant-design/pro-provider" "1.10.0"
    "@ant-design/pro-skeleton" "1.0.13"
    "@ant-design/pro-table" "2.79.0"
    "@ant-design/pro-utils" "1.44.0"
    "@babel/runtime" "^7.16.3"
    antd "^4.20.0 "

"@ant-design/pro-descriptions@1.11.16":
  version "1.11.16"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-descriptions/-/pro-descriptions-1.11.16.tgz#49109724448f0dc843817795c372c650c82d5608"
  integrity sha512-t3LV0oFanfTXV9+x06/3hyJBIewCwacaGJrA0SCLzp+2Ak3QhVnl8QCJR7qw5+lHkGer8wTwD6lf144TAVdPrQ==
  dependencies:
    "@ant-design/pro-field" "1.35.3"
    "@ant-design/pro-form" "1.73.0"
    "@ant-design/pro-skeleton" "1.0.13"
    "@ant-design/pro-utils" "1.44.0"
    "@babel/runtime" "^7.18.0"
    antd "^4.20.0 "
    rc-util "^5.0.6"
    use-json-comparison "^1.0.5"

"@ant-design/pro-field@1.35.3":
  version "1.35.3"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-field/-/pro-field-1.35.3.tgz#7f74046d714b1895270b63d74d0eb2ac2ce003cb"
  integrity sha512-DGusiIaXukyw5LfZU3FnNrGqoDt347OKCpgrl3/tgVO27Kl2A+5U6WKRiBV2iUc3MzddeD8xCbumkg6EAk2EyQ==
  dependencies:
    "@ant-design/icons" "^4.2.1"
    "@ant-design/pro-provider" "1.10.0"
    "@ant-design/pro-utils" "1.44.0"
    "@babel/runtime" "^7.18.0"
    "@chenshuai2144/sketch-color" "^1.0.8"
    antd "^4.20.0 "
    classnames "^2.2.6"
    lodash.tonumber "^4.0.3"
    moment "^2.27.0"
    omit.js "^2.0.2"
    rc-util "^5.4.0"
    swr "^1.2.0"

"@ant-design/pro-form@1.73.0":
  version "1.73.0"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-form/-/pro-form-1.73.0.tgz#f22afe546a4a66c48fdbb571758484f01faf5706"
  integrity sha512-oVOdzuRDB8FN5HbXNgdYre08AHjuqz6zEQiBhzyctYMcOOrLfloOCFeZlVMlKKwLvplXLNz3Ub3v704pFV0O9g==
  dependencies:
    "@ant-design/icons" "^4.2.1"
    "@ant-design/pro-field" "1.35.3"
    "@ant-design/pro-provider" "1.10.0"
    "@ant-design/pro-utils" "1.44.0"
    "@babel/runtime" "^7.18.0"
    "@umijs/use-params" "^1.0.9"
    antd "^4.20.0 "
    classnames "^2.2.6"
    lodash.merge "^4.6.2"
    omit.js "^2.0.2"
    rc-resize-observer "^1.1.0"
    rc-util "^5.0.6"
    use-json-comparison "^1.0.5"
    use-media-antd-query "^1.1.0"

"@ant-design/pro-layout@6.38.18":
  version "6.38.18"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-layout/-/pro-layout-6.38.18.tgz#39a83724b97d0b438c6fc74fa1f9ec199cea5393"
  integrity sha512-ItZBM4T5wp8VoR1xXodyH4EBYiaZFJeJhyfrE7MNJnqL00Yd8AsF+gIgh42aOB2XQZMRAz5P3AOk2HdYjOPVFA==
  dependencies:
    "@ant-design/icons" "^4.0.0"
    "@ant-design/pro-provider" "1.10.0"
    "@ant-design/pro-utils" "1.44.0"
    "@babel/runtime" "^7.18.0"
    "@umijs/route-utils" "^2.1.0"
    "@umijs/ssr-darkreader" "^4.9.44"
    "@umijs/use-params" "^1.0.9"
    antd "^4.20.0 "
    classnames "^2.2.6"
    lodash.merge "^4.6.2"
    omit.js "^2.0.2"
    path-to-regexp "2.4.0"
    rc-resize-observer "^1.1.0"
    rc-util "^5.0.6"
    swr "^1.2.0"
    unstated-next "^1.1.0"
    use-json-comparison "^1.0.3"
    use-media-antd-query "^1.1.0"
    warning "^4.0.3"

"@ant-design/pro-list@1.21.78":
  version "1.21.78"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-list/-/pro-list-1.21.78.tgz#85cb03d070a960fcb99485bc34233e41554157cf"
  integrity sha512-7anJxiMfBbvB6ypsIAgGjUdQiBdDgjZZwiX5uj5yA6xNlf+Dm4aYP2Kv4wuBCpG+cgn614vUO9koWtLu73vEuA==
  dependencies:
    "@ant-design/icons" "^4.0.0"
    "@ant-design/pro-card" "1.20.17"
    "@ant-design/pro-field" "1.35.3"
    "@ant-design/pro-table" "2.79.0"
    "@babel/runtime" "^7.18.0"
    antd "^4.20.0 "
    classnames "^2.2.6"
    moment "^2.24.0"
    rc-resize-observer "^1.0.0"
    rc-util "^4.19.0"
    unstated-next "^1.1.0"
    use-media-antd-query "^1.1.0"

"@ant-design/pro-provider@1.10.0":
  version "1.10.0"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-provider/-/pro-provider-1.10.0.tgz#6f15a5a7600d6ec7dd42755c4727254a9df46dbd"
  integrity sha512-gbQtq+Qlnob6aEghiWX/kXEzNhe4eOFlv5Ue2/xsal+22qvot/kFoOB652TD76fMI7vlkoema1pTKsvD5Uji6A==
  dependencies:
    "@babel/runtime" "^7.18.0"
    antd "^4.20.0 "
    rc-util "^5.0.1"
    swr "^1.2.0"

"@ant-design/pro-skeleton@1.0.13":
  version "1.0.13"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-skeleton/-/pro-skeleton-1.0.13.tgz#09443af2f104cca63ecf48b9c3713a9c431721b2"
  integrity sha512-EWkW7ZqnT7NsyJTmxd0btrRpJFNLBkE2ceaKgp2Cz7GOC0l8yWX/TaNs6yb10j0DH6AB9YBXZNd2mxZ2MyQgGA==
  dependencies:
    "@babel/runtime" "^7.18.0"
    antd "^4.20.0 "
    use-media-antd-query "^1.1.0"

"@ant-design/pro-table@2.79.0":
  version "2.79.0"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-table/-/pro-table-2.79.0.tgz#863538a3302c41d6f388e19395ba49a5223c8a4d"
  integrity sha512-LYlXpZLx7Ob3HG0rRgV29YJE5NbIaJLj9E6nNOYo3izxoz+QhaN3s/ydkQLdrZgTlPWBj9bT2vsRXBNnk3VuOA==
  dependencies:
    "@ant-design/icons" "^4.1.0"
    "@ant-design/pro-card" "1.20.17"
    "@ant-design/pro-field" "1.35.3"
    "@ant-design/pro-form" "1.73.0"
    "@ant-design/pro-provider" "1.10.0"
    "@ant-design/pro-utils" "1.44.0"
    "@babel/runtime" "^7.18.0"
    antd "^4.20.0 "
    classnames "^2.2.6"
    moment "^2.24.0"
    omit.js "^2.0.2"
    rc-util "^5.0.1"
    react-sortable-hoc "^2.0.0"
    unstated-next "^1.1.0"
    use-json-comparison "^1.0.5"
    use-media-antd-query "^1.1.0"

"@ant-design/pro-utils@1.44.0":
  version "1.44.0"
  resolved "https://registry.yarnpkg.com/@ant-design/pro-utils/-/pro-utils-1.44.0.tgz#d129774452748c680506a96db38388ff9063c4bc"
  integrity sha512-+r5KwzuCkp34p5Apudbe1wNGuSVNu2yrPX3TVAnFT8p7ZTg5iHLhNXOOcAKmON/Qktil7GUTW+v0t29/0fBy8Q==
  dependencies:
    "@ant-design/icons" "^4.3.0"
    "@ant-design/pro-provider" "1.10.0"
    "@babel/runtime" "^7.18.0"
    antd "^4.20.0 "
    classnames "^2.2.6"
    moment "^2.27.0"
    rc-util "^5.0.6"
    react-sortable-hoc "^2.0.0"
    swr "^1.2.0"

"@ant-design/react-slick@~0.29.1":
  version "0.29.2"
  resolved "https://registry.yarnpkg.com/@ant-design/react-slick/-/react-slick-0.29.2.tgz#53e6a7920ea3562eebb304c15a7fc2d7e619d29c"
  integrity sha512-kgjtKmkGHa19FW21lHnAfyyH9AAoh35pBdcJ53rHmQ3O+cfFHGHnUbj/HFrRNJ5vIts09FKJVAD8RpaC+RaWfA==
  dependencies:
    "@babel/runtime" "^7.10.4"
    classnames "^2.2.5"
    json2mq "^0.2.0"
    lodash "^4.17.21"
    resize-observer-polyfill "^1.5.1"

"@babel/code-frame@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
  integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
  dependencies:
    "@babel/highlight" "^7.18.6"

"@babel/compat-data@^7.18.8":
  version "7.18.8"
  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
  integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==

"@babel/core@^7.12.10", "@babel/core@^7.18.10":
  version "7.18.10"
  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8"
  integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==
  dependencies:
    "@ampproject/remapping" "^2.1.0"
    "@babel/code-frame" "^7.18.6"
    "@babel/generator" "^7.18.10"
    "@babel/helper-compilation-targets" "^7.18.9"
    "@babel/helper-module-transforms" "^7.18.9"
    "@babel/helpers" "^7.18.9"
    "@babel/parser" "^7.18.10"
    "@babel/template" "^7.18.10"
    "@babel/traverse" "^7.18.10"
    "@babel/types" "^7.18.10"
    convert-source-map "^1.7.0"
    debug "^4.1.0"
    gensync "^1.0.0-beta.2"
    json5 "^2.2.1"
    semver "^6.3.0"

"@babel/generator@^7.12.11", "@babel/generator@^7.18.10":
  version "7.18.12"
  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4"
  integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==
  dependencies:
    "@babel/types" "^7.18.10"
    "@jridgewell/gen-mapping" "^0.3.2"
    jsesc "^2.5.1"

"@babel/helper-annotate-as-pure@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb"
  integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==
  dependencies:
    "@babel/types" "^7.18.6"

"@babel/helper-compilation-targets@^7.18.9":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf"
  integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==
  dependencies:
    "@babel/compat-data" "^7.18.8"
    "@babel/helper-validator-option" "^7.18.6"
    browserslist "^4.20.2"
    semver "^6.3.0"

"@babel/helper-environment-visitor@^7.18.9":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
  integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==

"@babel/helper-function-name@^7.18.9":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0"
  integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==
  dependencies:
    "@babel/template" "^7.18.6"
    "@babel/types" "^7.18.9"

"@babel/helper-hoist-variables@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
  integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
  dependencies:
    "@babel/types" "^7.18.6"

"@babel/helper-module-imports@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
  integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
  dependencies:
    "@babel/types" "^7.18.6"

"@babel/helper-module-transforms@^7.18.9":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712"
  integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==
  dependencies:
    "@babel/helper-environment-visitor" "^7.18.9"
    "@babel/helper-module-imports" "^7.18.6"
    "@babel/helper-simple-access" "^7.18.6"
    "@babel/helper-split-export-declaration" "^7.18.6"
    "@babel/helper-validator-identifier" "^7.18.6"
    "@babel/template" "^7.18.6"
    "@babel/traverse" "^7.18.9"
    "@babel/types" "^7.18.9"

"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
  integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==

"@babel/helper-simple-access@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea"
  integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==
  dependencies:
    "@babel/types" "^7.18.6"

"@babel/helper-split-export-declaration@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
  integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
  dependencies:
    "@babel/types" "^7.18.6"

"@babel/helper-string-parser@^7.18.10":
  version "7.18.10"
  resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56"
  integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==

"@babel/helper-validator-identifier@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
  integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==

"@babel/helper-validator-option@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
  integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==

"@babel/helpers@^7.18.9":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9"
  integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==
  dependencies:
    "@babel/template" "^7.18.6"
    "@babel/traverse" "^7.18.9"
    "@babel/types" "^7.18.9"

"@babel/highlight@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
  integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
  dependencies:
    "@babel/helper-validator-identifier" "^7.18.6"
    chalk "^2.0.0"
    js-tokens "^4.0.0"

"@babel/parser@^7.12.11", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11":
  version "7.18.11"
  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9"
  integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==

"@babel/plugin-syntax-jsx@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
  integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==
  dependencies:
    "@babel/helper-plugin-utils" "^7.18.6"

"@babel/plugin-transform-react-jsx-development@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.18.6.tgz#dbe5c972811e49c7405b630e4d0d2e1380c0ddc5"
  integrity sha512-SA6HEjwYFKF7WDjWcMcMGUimmw/nhNRDWxr+KaLSCrkD/LMDBvWRmHAYgE1HDeF8KUuI8OAu+RT6EOtKxSW2qA==
  dependencies:
    "@babel/plugin-transform-react-jsx" "^7.18.6"

"@babel/plugin-transform-react-jsx-self@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.18.6.tgz#3849401bab7ae8ffa1e3e5687c94a753fc75bda7"
  integrity sha512-A0LQGx4+4Jv7u/tWzoJF7alZwnBDQd6cGLh9P+Ttk4dpiL+J5p7NSNv/9tlEFFJDq3kjxOavWmbm6t0Gk+A3Ig==
  dependencies:
    "@babel/helper-plugin-utils" "^7.18.6"

"@babel/plugin-transform-react-jsx-source@^7.18.6":
  version "7.18.6"
  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.18.6.tgz#06e9ae8a14d2bc19ce6e3c447d842032a50598fc"
  integrity sha512-utZmlASneDfdaMh0m/WausbjUjEdGrQJz0vFK93d7wD3xf5wBtX219+q6IlCNZeguIcxS2f/CvLZrlLSvSHQXw==
  dependencies:
    "@babel/helper-plugin-utils" "^7.18.6"

"@babel/plugin-transform-react-jsx@^7.18.10", "@babel/plugin-transform-react-jsx@^7.18.6":
  version "7.18.10"
  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.18.10.tgz#ea47b2c4197102c196cbd10db9b3bb20daa820f1"
  integrity sha512-gCy7Iikrpu3IZjYZolFE4M1Sm+nrh1/6za2Ewj77Z+XirT4TsbJcvOFOyF+fRPwU6AKKK136CZxx6L8AbSFG6A==
  dependencies:
    "@babel/helper-annotate-as-pure" "^7.18.6"
    "@babel/helper-module-imports" "^7.18.6"
    "@babel/helper-plugin-utils" "^7.18.9"
    "@babel/plugin-syntax-jsx" "^7.18.6"
    "@babel/types" "^7.18.10"

"@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.1", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.16.3", "@babel/runtime@^7.18.0", "@babel/runtime@^7.18.3", "@babel/runtime@^7.2.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6":
  version "7.18.9"
  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a"
  integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==
  dependencies:
    regenerator-runtime "^0.13.4"

"@babel/template@^7.18.10", "@babel/template@^7.18.6":
  version "7.18.10"
  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71"
  integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==
  dependencies:
    "@babel/code-frame" "^7.18.6"
    "@babel/parser" "^7.18.10"
    "@babel/types" "^7.18.10"

"@babel/traverse@^7.12.12", "@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9":
  version "7.18.11"
  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f"
  integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==
  dependencies:
    "@babel/code-frame" "^7.18.6"
    "@babel/generator" "^7.18.10"
    "@babel/helper-environment-visitor" "^7.18.9"
    "@babel/helper-function-name" "^7.18.9"
    "@babel/helper-hoist-variables" "^7.18.6"
    "@babel/helper-split-export-declaration" "^7.18.6"
    "@babel/parser" "^7.18.11"
    "@babel/types" "^7.18.10"
    debug "^4.1.0"
    globals "^11.1.0"

"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9":
  version "7.18.10"
  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6"
  integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==
  dependencies:
    "@babel/helper-string-parser" "^7.18.10"
    "@babel/helper-validator-identifier" "^7.18.6"
    to-fast-properties "^2.0.0"

"@chenshuai2144/sketch-color@^1.0.8":
  version "1.0.8"
  resolved "https://registry.yarnpkg.com/@chenshuai2144/sketch-color/-/sketch-color-1.0.8.tgz#5cde7ac1dae5687223a8f751b0e4f77a5776d566"
  integrity sha512-dPAzzWc+w7zyTAi71WXYZpiTYyIS80MxYyy2E/7jufhnJI1Z29wCPL35VvuJ/gs5zYpF2+w/B7BizWa2zKXpGw==
  dependencies:
    reactcss "^1.2.3"
    tinycolor2 "^1.4.2"

"@ctrl/tinycolor@^3.4.0":
  version "3.4.1"
  resolved "https://registry.yarnpkg.com/@ctrl/tinycolor/-/tinycolor-3.4.1.tgz#75b4c27948c81e88ccd3a8902047bcd797f38d32"
  integrity sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==

"@cush/relative@^1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@cush/relative/-/relative-1.0.0.tgz#8cd1769bf9bde3bb27dac356b1bc94af40f6cc16"
  integrity sha512-RpfLEtTlyIxeNPGKcokS+p3BZII/Q3bYxryFRglh5H3A3T8q9fsLYm72VYAMEOOIBLEa8o93kFLiBDUWKrwXZA==

"@esbuild/linux-loong64@0.14.54":
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028"
  integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==

"@floating-ui/core@^0.7.3":
  version "0.7.3"
  resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-0.7.3.tgz#d274116678ffae87f6b60e90f88cc4083eefab86"
  integrity sha512-buc8BXHmG9l82+OQXOFU3Kr2XQx9ys01U/Q9HMIrZ300iLc8HLMgh7dcCqgYzAzf4BkoQvDcXf5Y+CuEZ5JBYg==

"@floating-ui/dom@^0.5.3":
  version "0.5.4"
  resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-0.5.4.tgz#4eae73f78bcd4bd553ae2ade30e6f1f9c73fe3f1"
  integrity sha512-419BMceRLq0RrmTSDxn8hf9R3VCJv2K9PUfugh5JyEFmdjzDo+e8U5EdR8nzKq8Yj1htzLm3b6eQEEam3/rrtg==
  dependencies:
    "@floating-ui/core" "^0.7.3"

"@floating-ui/react-dom@0.7.2":
  version "0.7.2"
  resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-0.7.2.tgz#0bf4ceccb777a140fc535c87eb5d6241c8e89864"
  integrity sha512-1T0sJcpHgX/u4I1OzIEhlcrvkUN8ln39nz7fMoE/2HDHrPiMFoOGR7++GYyfUmIQHkkrTinaeQsO3XWubjSvGg==
  dependencies:
    "@floating-ui/dom" "^0.5.3"
    use-isomorphic-layout-effect "^1.1.1"

"@jridgewell/gen-mapping@^0.1.0":
  version "0.1.1"
  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
  integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
  dependencies:
    "@jridgewell/set-array" "^1.0.0"
    "@jridgewell/sourcemap-codec" "^1.4.10"

"@jridgewell/gen-mapping@^0.3.2":
  version "0.3.2"
  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
  integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
  dependencies:
    "@jridgewell/set-array" "^1.0.1"
    "@jridgewell/sourcemap-codec" "^1.4.10"
    "@jridgewell/trace-mapping" "^0.3.9"

"@jridgewell/resolve-uri@^3.0.3":
  version "3.1.0"
  resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
  integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==

"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
  version "1.1.2"
  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==

"@jridgewell/sourcemap-codec@^1.4.10":
  version "1.4.14"
  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
  integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==

"@jridgewell/trace-mapping@^0.3.9":
  version "0.3.15"
  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774"
  integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==
  dependencies:
    "@jridgewell/resolve-uri" "^3.0.3"
    "@jridgewell/sourcemap-codec" "^1.4.10"

"@nodelib/fs.scandir@2.1.5":
  version "2.1.5"
  resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
  integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
  dependencies:
    "@nodelib/fs.stat" "2.0.5"
    run-parallel "^1.1.9"

"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
  version "2.0.5"
  resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
  integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==

"@nodelib/fs.walk@^1.2.3":
  version "1.2.8"
  resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
  integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
  dependencies:
    "@nodelib/fs.scandir" "2.1.5"
    fastq "^1.6.0"

"@qixian.cs/path-to-regexp@^6.1.0":
  version "6.1.0"
  resolved "https://registry.yarnpkg.com/@qixian.cs/path-to-regexp/-/path-to-regexp-6.1.0.tgz#6b84ad01596332aba95fa29d2e70104698cd5c45"
  integrity sha512-2jIiLiVZB1jnY7IIRQKtoV8Gnr7XIhk4mC88ONGunZE3hYt5IHUG4BE/6+JiTBjjEWQLBeWnZB8hGpppkufiVw==

"@radix-ui/primitive@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.0.tgz#e1d8ef30b10ea10e69c76e896f608d9276352253"
  integrity sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==
  dependencies:
    "@babel/runtime" "^7.13.10"

"@radix-ui/react-arrow@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.0.tgz#c461f4c2cab3317e3d42a1ae62910a4cbb0192a1"
  integrity sha512-1MUuv24HCdepi41+qfv125EwMuxgQ+U+h0A9K3BjCO/J8nVRREKHHpkD9clwfnjEDk9hgGzCnff4aUKCPiRepw==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-primitive" "1.0.0"

"@radix-ui/react-collection@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.0.tgz#0ec4c72fabd35a03b5787075ac799e3b17ca5710"
  integrity sha512-8i1pf5dKjnq90Z8udnnXKzdCEV3/FYrfw0n/b6NvB6piXEn3fO1bOh7HBcpG8XrnIXzxlYu2oCcR38QpyLS/mg==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-compose-refs" "1.0.0"
    "@radix-ui/react-context" "1.0.0"
    "@radix-ui/react-primitive" "1.0.0"
    "@radix-ui/react-slot" "1.0.0"

"@radix-ui/react-compose-refs@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz#37595b1f16ec7f228d698590e78eeed18ff218ae"
  integrity sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==
  dependencies:
    "@babel/runtime" "^7.13.10"

"@radix-ui/react-context@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.0.tgz#f38e30c5859a9fb5e9aa9a9da452ee3ed9e0aee0"
  integrity sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==
  dependencies:
    "@babel/runtime" "^7.13.10"

"@radix-ui/react-dismissable-layer@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz#35b7826fa262fd84370faef310e627161dffa76b"
  integrity sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/primitive" "1.0.0"
    "@radix-ui/react-compose-refs" "1.0.0"
    "@radix-ui/react-primitive" "1.0.0"
    "@radix-ui/react-use-callback-ref" "1.0.0"
    "@radix-ui/react-use-escape-keydown" "1.0.0"

"@radix-ui/react-focus-guards@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.0.tgz#339c1c69c41628c1a5e655f15f7020bf11aa01fa"
  integrity sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==
  dependencies:
    "@babel/runtime" "^7.13.10"

"@radix-ui/react-focus-scope@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.0.0.tgz#95a0c1188276dc8933b1eac5f1cdb6471e01ade5"
  integrity sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-compose-refs" "1.0.0"
    "@radix-ui/react-primitive" "1.0.0"
    "@radix-ui/react-use-callback-ref" "1.0.0"

"@radix-ui/react-id@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.0.tgz#8d43224910741870a45a8c9d092f25887bb6d11e"
  integrity sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-use-layout-effect" "1.0.0"

"@radix-ui/react-popover@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.0.0.tgz#5ee72013089fdf9038417fc1eb98a749c17457fd"
  integrity sha512-osxFFO0TiZ9ABpEOitZu0R1Fdd+tSpJgAqLZxRLLdZQ7ya0onSODcITp5hXDVuYQeVXH6pKEBGwXN6ZGjZ0a5g==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/primitive" "1.0.0"
    "@radix-ui/react-compose-refs" "1.0.0"
    "@radix-ui/react-context" "1.0.0"
    "@radix-ui/react-dismissable-layer" "1.0.0"
    "@radix-ui/react-focus-guards" "1.0.0"
    "@radix-ui/react-focus-scope" "1.0.0"
    "@radix-ui/react-id" "1.0.0"
    "@radix-ui/react-popper" "1.0.0"
    "@radix-ui/react-portal" "1.0.0"
    "@radix-ui/react-presence" "1.0.0"
    "@radix-ui/react-primitive" "1.0.0"
    "@radix-ui/react-slot" "1.0.0"
    "@radix-ui/react-use-controllable-state" "1.0.0"
    aria-hidden "^1.1.1"
    react-remove-scroll "2.5.4"

"@radix-ui/react-popper@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.0.0.tgz#fb4f937864bf39c48f27f55beee61fa9f2bef93c"
  integrity sha512-k2dDd+1Wl0XWAMs9ZvAxxYsB9sOsEhrFQV4CINd7IUZf0wfdye4OHen9siwxvZImbzhgVeKTJi68OQmPRvVdMg==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@floating-ui/react-dom" "0.7.2"
    "@radix-ui/react-arrow" "1.0.0"
    "@radix-ui/react-compose-refs" "1.0.0"
    "@radix-ui/react-context" "1.0.0"
    "@radix-ui/react-primitive" "1.0.0"
    "@radix-ui/react-use-layout-effect" "1.0.0"
    "@radix-ui/react-use-rect" "1.0.0"
    "@radix-ui/react-use-size" "1.0.0"
    "@radix-ui/rect" "1.0.0"

"@radix-ui/react-portal@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.0.tgz#7220b66743394fabb50c55cb32381395cc4a276b"
  integrity sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-primitive" "1.0.0"

"@radix-ui/react-presence@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.0.0.tgz#814fe46df11f9a468808a6010e3f3ca7e0b2e84a"
  integrity sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-compose-refs" "1.0.0"
    "@radix-ui/react-use-layout-effect" "1.0.0"

"@radix-ui/react-primitive@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.0.tgz#376cd72b0fcd5e0e04d252ed33eb1b1f025af2b0"
  integrity sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-slot" "1.0.0"

"@radix-ui/react-slot@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.0.0.tgz#7fa805b99891dea1e862d8f8fbe07f4d6d0fd698"
  integrity sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-compose-refs" "1.0.0"

"@radix-ui/react-toast@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.0.0.tgz#0d808e7f13376ddc74c0dbb7a4ff1c0602778ae2"
  integrity sha512-mdoF6rahgushdev0OX+9a7JKoH0xZAZBo2Ktf/s779S7EnkZeL3/MFiRIV5LpRP5CtASmfdSD3FLnEvG1RHRtQ==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/primitive" "1.0.0"
    "@radix-ui/react-collection" "1.0.0"
    "@radix-ui/react-compose-refs" "1.0.0"
    "@radix-ui/react-context" "1.0.0"
    "@radix-ui/react-dismissable-layer" "1.0.0"
    "@radix-ui/react-portal" "1.0.0"
    "@radix-ui/react-presence" "1.0.0"
    "@radix-ui/react-primitive" "1.0.0"
    "@radix-ui/react-use-callback-ref" "1.0.0"
    "@radix-ui/react-use-controllable-state" "1.0.0"
    "@radix-ui/react-use-layout-effect" "1.0.0"
    "@radix-ui/react-visually-hidden" "1.0.0"

"@radix-ui/react-use-callback-ref@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.0.tgz#9e7b8b6b4946fe3cbe8f748c82a2cce54e7b6a90"
  integrity sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==
  dependencies:
    "@babel/runtime" "^7.13.10"

"@radix-ui/react-use-controllable-state@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f"
  integrity sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-use-callback-ref" "1.0.0"

"@radix-ui/react-use-escape-keydown@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz#aef375db4736b9de38a5a679f6f49b45a060e5d1"
  integrity sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-use-callback-ref" "1.0.0"

"@radix-ui/react-use-layout-effect@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.0.0.tgz#2fc19e97223a81de64cd3ba1dc42ceffd82374dc"
  integrity sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==
  dependencies:
    "@babel/runtime" "^7.13.10"

"@radix-ui/react-use-rect@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.0.tgz#b040cc88a4906b78696cd3a32b075ed5b1423b3e"
  integrity sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/rect" "1.0.0"

"@radix-ui/react-use-size@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.0.tgz#a0b455ac826749419f6354dc733e2ca465054771"
  integrity sha512-imZ3aYcoYCKhhgNpkNDh/aTiU05qw9hX+HHI1QDBTyIlcFjgeFlKKySNGMwTp7nYFLQg/j0VA2FmCY4WPDDHMg==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-use-layout-effect" "1.0.0"

"@radix-ui/react-visually-hidden@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.0.tgz#4d69d7e3b6d21ee4678ed6de5215dcd068394401"
  integrity sha512-MwAhMdX+n6S4InwRKSnpUsp+lLkYG6izQF56ul6guSX2mBBLOMV9Frx7xJlkEe2GjKLzbNuHhaCS6e5gopmZNA==
  dependencies:
    "@babel/runtime" "^7.13.10"
    "@radix-ui/react-primitive" "1.0.0"

"@radix-ui/rect@1.0.0":
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.0.tgz#0dc8e6a829ea2828d53cbc94b81793ba6383bf3c"
  integrity sha512-d0O68AYy/9oeEy1DdC07bz1/ZXX+DqCskRd3i4JzLSTXwefzaepQrKjXC7aNM8lTHjFLDO0pDgaEiQ7jEk+HVg==
  dependencies:
    "@babel/runtime" "^7.13.10"

"@tanstack/match-sorter-utils@^8.0.0-alpha.82":
  version "8.1.1"
  resolved "https://registry.yarnpkg.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.1.1.tgz#895f407813254a46082a6bbafad9b39b943dc834"
  integrity sha512-IdmEekEYxQsoLOR0XQyw3jD1GujBpRRYaGJYQUw1eOT1eUugWxdc7jomh1VQ1EKHcdwDLpLaCz/8y4KraU4T9A==
  dependencies:
    remove-accents "0.4.2"

"@tanstack/react-query-devtools@4.2.1":
  version "4.2.1"
  resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-4.2.1.tgz#decee3d1d174b253fa303d5baaa478fb0e2c0e63"
  integrity sha512-k7Ch3qvs8U74aRMMRvNisxcxZFTzk8FDdvpQKXxSZ8fsD4ZwpM0MoUSqKsCXbfTvUI7MJiGxavy1YlvImPNO+Q==
  dependencies:
    "@tanstack/match-sorter-utils" "^8.0.0-alpha.82"
    "@types/use-sync-external-store" "^0.0.3"
    use-sync-external-store "^1.2.0"

"@types/cookie@^0.3.3":
  version "0.3.3"
  resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.3.3.tgz#85bc74ba782fb7aa3a514d11767832b0e3bc6803"
  integrity sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==

"@types/node@18.7.3":
  version "18.7.3"
  resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.3.tgz#432c89796eab539b7a30b7b8801a727b585238a4"
  integrity sha512-LJgzOEwWuMTBxHzgBR/fhhBOWrvBjvO+zPteUgbbuQi80rYIZHrk1mNbRUqPZqSLP2H7Rwt1EFLL/tNLD1Xx/w==

"@types/prop-types@*":
  version "15.7.5"
  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf"
  integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==

"@types/react-dom@18.0.6":
  version "18.0.6"
  resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1"
  integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA==
  dependencies:
    "@types/react" "*"

"@types/react@*", "@types/react@18.0.17":
  version "18.0.17"
  resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.17.tgz#4583d9c322d67efe4b39a935d223edcc7050ccf4"
  integrity sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ==
  dependencies:
    "@types/prop-types" "*"
    "@types/scheduler" "*"
    csstype "^3.0.2"

"@types/scheduler@*":
  version "0.16.2"
  resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
  integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==

"@types/use-sync-external-store@^0.0.3":
  version "0.0.3"
  resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
  integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==

"@umijs/route-utils@^2.1.0":
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/@umijs/route-utils/-/route-utils-2.2.0.tgz#f76a02d2fc19ce70f9ad17308f80a4f3550ee65b"
  integrity sha512-1t/A5+KuoK+AX0FcddNbUKMpMskuDsSSZRlQoTXDBBTDKUgWwG8KSX/NnfTCYJQrntL4A9WYymygop1jAw+Wdw==
  dependencies:
    "@qixian.cs/path-to-regexp" "^6.1.0"
    fast-deep-equal "^3.1.3"
    lodash.isequal "^4.5.0"
    memoize-one "^5.1.1"

"@umijs/ssr-darkreader@^4.9.44":
  version "4.9.45"
  resolved "https://registry.yarnpkg.com/@umijs/ssr-darkreader/-/ssr-darkreader-4.9.45.tgz#7f13cfc89616f3203290775eb61b53047ece1733"
  integrity sha512-XlcwzSYQ/SRZpHdwIyMDS4FOGX5kP4U/2g2mykyn/iPQTK4xTiQAyBu6UnnDnn7d5P8s7Atzh1C7H0ETNOypJg==

"@umijs/use-params@^1.0.9":
  version "1.0.9"
  resolved "https://registry.yarnpkg.com/@umijs/use-params/-/use-params-1.0.9.tgz#0ae4a87f4922d8e8e3fb4495b0f8f4de9ca38c52"
  integrity sha512-QlN0RJSBVQBwLRNxbxjQ5qzqYIGn+K7USppMoIOVlf7fxXHsnQZ2bEsa6Pm74bt6DVQxpUE8HqvdStn6Y9FV1w==

"@vitejs/plugin-react@2.0.1":
  version "2.0.1"
  resolved "https://registry.yarnpkg.com/@vitejs/plugin-react/-/plugin-react-2.0.1.tgz#3197c01d8e4a4eb9fed829c7888c467a43aadd4e"
  integrity sha512-uINzNHmjrbunlFtyVkST6lY1ewSfz/XwLufG0PIqvLGnpk2nOIOa/1CACTDNcKi1/RwaCzJLmsXwm1NsUVV/NA==
  dependencies:
    "@babel/core" "^7.18.10"
    "@babel/plugin-transform-react-jsx" "^7.18.10"
    "@babel/plugin-transform-react-jsx-development" "^7.18.6"
    "@babel/plugin-transform-react-jsx-self" "^7.18.6"
    "@babel/plugin-transform-react-jsx-source" "^7.18.6"
    magic-string "^0.26.2"
    react-refresh "^0.14.0"

acorn-node@^1.8.2:
  version "1.8.2"
  resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
  integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
  dependencies:
    acorn "^7.0.0"
    acorn-walk "^7.0.0"
    xtend "^4.0.2"

acorn-walk@^7.0.0:
  version "7.2.0"
  resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
  integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==

acorn@^7.0.0:
  version "7.4.1"
  resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
  integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==

add-dom-event-listener@^1.1.0:
  version "1.1.0"
  resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.1.0.tgz#6a92db3a0dd0abc254e095c0f1dc14acbbaae310"
  integrity sha512-WCxx1ixHT0GQU9hb0KI/mhgRQhnU+U3GvwY6ZvVjYq8rsihIGoaIOUbY0yMPBxLH5MDtr0kz3fisWGNcbWW7Jw==
  dependencies:
    object-assign "4.x"

ansi-styles@^3.2.1:
  version "3.2.1"
  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
  integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
  dependencies:
    color-convert "^1.9.0"

ansi-styles@^4.1.0:
  version "4.3.0"
  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
  integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
  dependencies:
    color-convert "^2.0.1"

antd@4.22.4:
  version "4.22.4"
  resolved "https://registry.yarnpkg.com/antd/-/antd-4.22.4.tgz#38b375f93ac5f5e3e6b73bb7029f9c38f8ed157f"
  integrity sha512-u7l68PhSJMxyZmCNw+jwaeurSAaE+bU+lB0SPRHLfOFJR6xRudntvRoQdiEFOD5FZ3eeqz4lYQ3RZZRIarmceg==
  dependencies:
    "@ant-design/colors" "^6.0.0"
    "@ant-design/icons" "^4.7.0"
    "@ant-design/react-slick" "~0.29.1"
    "@babel/runtime" "^7.18.3"
    "@ctrl/tinycolor" "^3.4.0"
    classnames "^2.2.6"
    copy-to-clipboard "^3.2.0"
    lodash "^4.17.21"
    memoize-one "^6.0.0"
    moment "^2.29.2"
    rc-cascader "~3.6.0"
    rc-checkbox "~2.3.0"
    rc-collapse "~3.3.0"
    rc-dialog "~8.9.0"
    rc-drawer "~5.1.0-alpha.1"
    rc-dropdown "~4.0.0"
    rc-field-form "~1.27.0"
    rc-image "~5.7.0"
    rc-input "~0.0.1-alpha.5"
    rc-input-number "~7.3.5"
    rc-mentions "~1.9.1"
    rc-menu "~9.6.0"
    rc-motion "^2.6.1"
    rc-notification "~4.6.0"
    rc-pagination "~3.1.17"
    rc-picker "~2.6.10"
    rc-progress "~3.3.2"
    rc-rate "~2.9.0"
    rc-resize-observer "^1.2.0"
    rc-segmented "~2.1.0"
    rc-select "~14.1.1"
    rc-slider "~10.0.0"
    rc-steps "~4.1.0"
    rc-switch "~3.2.0"
    rc-table "~7.25.3"
    rc-tabs "~11.16.0"
    rc-textarea "~0.3.0"
    rc-tooltip "~5.2.0"
    rc-tree "~5.6.5"
    rc-tree-select "~5.4.0"
    rc-trigger "^5.2.10"
    rc-upload "~4.3.0"
    rc-util "^5.22.5"
    scroll-into-view-if-needed "^2.2.25"

"antd@^4.20.0 ":
  version "4.22.6"
  resolved "https://registry.yarnpkg.com/antd/-/antd-4.22.6.tgz#f9b90072ab2550f931def2e2e93b26281760529e"
  integrity sha512-cyGq4CFrKcHISkyvXHODqhV8X0W7r6GHP37H/KvvmYibz/1sdts3Sa0FoAPy/c165TjAcSC/ZHxJhuGbo42zvA==
  dependencies:
    "@ant-design/colors" "^6.0.0"
    "@ant-design/icons" "^4.7.0"
    "@ant-design/react-slick" "~0.29.1"
    "@babel/runtime" "^7.18.3"
    "@ctrl/tinycolor" "^3.4.0"
    classnames "^2.2.6"
    copy-to-clipboard "^3.2.0"
    lodash "^4.17.21"
    memoize-one "^6.0.0"
    moment "^2.29.2"
    rc-cascader "~3.6.0"
    rc-checkbox "~2.3.0"
    rc-collapse "~3.3.0"
    rc-dialog "~8.9.0"
    rc-drawer "~5.1.0"
    rc-dropdown "~4.0.0"
    rc-field-form "~1.27.0"
    rc-image "~5.7.0"
    rc-input "~0.0.1-alpha.5"
    rc-input-number "~7.3.5"
    rc-mentions "~1.9.1"
    rc-menu "~9.6.3"
    rc-motion "^2.6.1"
    rc-notification "~4.6.0"
    rc-pagination "~3.1.17"
    rc-picker "~2.6.10"
    rc-progress "~3.3.2"
    rc-rate "~2.9.0"
    rc-resize-observer "^1.2.0"
    rc-segmented "~2.1.0"
    rc-select "~14.1.1"
    rc-slider "~10.0.0"
    rc-steps "~4.1.0"
    rc-switch "~3.2.0"
    rc-table "~7.25.3"
    rc-tabs "~11.16.0"
    rc-textarea "~0.3.0"
    rc-tooltip "~5.2.0"
    rc-tree "~5.6.5"
    rc-tree-select "~5.4.0"
    rc-trigger "^5.2.10"
    rc-upload "~4.3.0"
    rc-util "^5.22.5"
    scroll-into-view-if-needed "^2.2.25"

anymatch@~3.1.2:
  version "3.1.2"
  resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
  integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
  dependencies:
    normalize-path "^3.0.0"
    picomatch "^2.0.4"

arg@^5.0.2:
  version "5.0.2"
  resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
  integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==

aria-hidden@^1.1.1:
  version "1.1.3"
  resolved "https://registry.yarnpkg.com/aria-hidden/-/aria-hidden-1.1.3.tgz#bb48de18dc84787a3c6eee113709c473c64ec254"
  integrity sha512-RhVWFtKH5BiGMycI72q2RAFMLQi8JP9bLuQXgR5a8Znp7P5KOIADSJeyfI8PCVxLEp067B2HbP5JIiI/PXIZeA==
  dependencies:
    tslib "^1.0.0"

array-tree-filter@^2.1.0:
  version "2.1.0"
  resolved "https://registry.yarnpkg.com/array-tree-filter/-/array-tree-filter-2.1.0.tgz#873ac00fec83749f255ac8dd083814b4f6329190"
  integrity sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==

async-validator@^4.1.0:
  version "4.2.5"
  resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339"
  integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==

asynckit@^0.4.0:
  version "0.4.0"
  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==

autoprefixer@10.4.8:
  version "10.4.8"
  resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.8.tgz#92c7a0199e1cfb2ad5d9427bd585a3d75895b9e5"
  integrity sha512-75Jr6Q/XpTqEf6D2ltS5uMewJIx5irCU1oBYJrWjFenq/m12WRRrz6g15L1EIoYvPLXTbEry7rDOwrcYNj77xw==
  dependencies:
    browserslist "^4.21.3"
    caniuse-lite "^1.0.30001373"
    fraction.js "^4.2.0"
    normalize-range "^0.1.2"
    picocolors "^1.0.0"
    postcss-value-parser "^4.2.0"

axios@0.27.2:
  version "0.27.2"
  resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
  integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
  dependencies:
    follow-redirects "^1.14.9"
    form-data "^4.0.0"

balanced-match@^1.0.0:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
  integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==

big-integer@^1.6.16:
  version "1.6.51"
  resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
  integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==

binary-extensions@^2.0.0:
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
  integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==

brace-expansion@^1.1.7:
  version "1.1.11"
  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
  dependencies:
    balanced-match "^1.0.0"
    concat-map "0.0.1"

braces@^3.0.2, braces@~3.0.2:
  version "3.0.2"
  resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
  integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
  dependencies:
    fill-range "^7.0.1"

broadcast-channel@^3.4.1:
  version "3.7.0"
  resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
  integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
  dependencies:
    "@babel/runtime" "^7.7.2"
    detect-node "^2.1.0"
    js-sha3 "0.8.0"
    microseconds "0.2.0"
    nano-time "1.0.0"
    oblivious-set "1.0.0"
    rimraf "3.0.2"
    unload "2.2.0"

browserslist@^4.20.2, browserslist@^4.21.3:
  version "4.21.3"
  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a"
  integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==
  dependencies:
    caniuse-lite "^1.0.30001370"
    electron-to-chromium "^1.4.202"
    node-releases "^2.0.6"
    update-browserslist-db "^1.0.5"

camelcase-css@^2.0.1:
  version "2.0.1"
  resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
  integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==

caniuse-lite@^1.0.30001370, caniuse-lite@^1.0.30001373:
  version "1.0.30001375"
  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz#8e73bc3d1a4c800beb39f3163bf0190d7e5d7672"
  integrity sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw==

chalk@^2.0.0:
  version "2.4.2"
  resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
  integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
  dependencies:
    ansi-styles "^3.2.1"
    escape-string-regexp "^1.0.5"
    supports-color "^5.3.0"

chalk@^4.1.0:
  version "4.1.2"
  resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
  integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
  dependencies:
    ansi-styles "^4.1.0"
    supports-color "^7.1.0"

chokidar@^3.5.3:
  version "3.5.3"
  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
  dependencies:
    anymatch "~3.1.2"
    braces "~3.0.2"
    glob-parent "~5.1.2"
    is-binary-path "~2.1.0"
    is-glob "~4.0.1"
    normalize-path "~3.0.0"
    readdirp "~3.6.0"
  optionalDependencies:
    fsevents "~2.3.2"

classnames@2.x, classnames@^2.2.1, classnames@^2.2.3, classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1:
  version "2.3.1"
  resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.1.tgz#dfcfa3891e306ec1dad105d0e88f4417b8535e8e"
  integrity sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==

color-convert@^1.9.0:
  version "1.9.3"
  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
  integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
  dependencies:
    color-name "1.1.3"

color-convert@^2.0.1:
  version "2.0.1"
  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
  integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
  dependencies:
    color-name "~1.1.4"

color-name@1.1.3:
  version "1.1.3"
  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
  integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==

color-name@^1.1.4, color-name@~1.1.4:
  version "1.1.4"
  resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
  integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==

combined-stream@^1.0.8:
  version "1.0.8"
  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
  dependencies:
    delayed-stream "~1.0.0"

compute-scroll-into-view@^1.0.17:
  version "1.0.17"
  resolved "https://registry.yarnpkg.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.17.tgz#6a88f18acd9d42e9cf4baa6bec7e0522607ab7ab"
  integrity sha512-j4dx+Fb0URmzbwwMUrhqWM2BEWHdFGx+qZ9qqASHRPqvTYdqvWnHg0H1hIbcyLnvgnoNAVMlwkepyqM3DaIFUg==

concat-map@0.0.1:
  version "0.0.1"
  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==

convert-source-map@^1.7.0:
  version "1.8.0"
  resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
  integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
  dependencies:
    safe-buffer "~5.1.1"

cookie@^0.4.0:
  version "0.4.2"
  resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
  integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==

copy-anything@^2.0.1:
  version "2.0.6"
  resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480"
  integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==
  dependencies:
    is-what "^3.14.1"

copy-to-clipboard@^3.2.0:
  version "3.3.2"
  resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz#5b263ec2366224b100181dded7ce0579b340c107"
  integrity sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg==
  dependencies:
    toggle-selection "^1.0.6"

cssesc@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
  integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==

csstype@^3.0.2:
  version "3.1.0"
  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2"
  integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==

date-fns@2.x:
  version "2.29.1"
  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.1.tgz#9667c2615525e552b5135a3116b95b1961456e60"
  integrity sha512-dlLD5rKaKxpFdnjrs+5azHDFOPEu4ANy/LTh04A1DTzMM7qoajmKCBc8pkKRFT41CNzw+4gQh79X5C+Jq27HAw==

dayjs@1.x:
  version "1.11.5"
  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93"
  integrity sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==

debug@^3.2.6:
  version "3.2.7"
  resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
  integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
  dependencies:
    ms "^2.1.1"

debug@^4.1.0, debug@^4.1.1:
  version "4.3.4"
  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
  dependencies:
    ms "2.1.2"

defined@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
  integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==

delayed-stream@~1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==

detect-node-es@^1.1.0:
  version "1.1.0"
  resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493"
  integrity sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==

detect-node@^2.0.4, detect-node@^2.1.0:
  version "2.1.0"
  resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
  integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==

detective@^5.2.1:
  version "5.2.1"
  resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034"
  integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==
  dependencies:
    acorn-node "^1.8.2"
    defined "^1.0.0"
    minimist "^1.2.6"

didyoumean@^1.2.2:
  version "1.2.2"
  resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
  integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==

dlv@^1.1.3:
  version "1.1.3"
  resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
  integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==

dom-align@^1.7.0:
  version "1.12.3"
  resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.12.3.tgz#a36d02531dae0eefa2abb0c4db6595250526f103"
  integrity sha512-Gj9hZN3a07cbR6zviMUBOMPdWxYhbMI+x+WS0NAIu2zFZmbK8ys9R79g+iG9qLnlCwpFoaB+fKy8Pdv470GsPA==

dot-case@^3.0.4:
  version "3.0.4"
  resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
  integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
  dependencies:
    no-case "^3.0.4"
    tslib "^2.0.3"

electron-to-chromium@^1.4.202:
  version "1.4.218"
  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.218.tgz#d6b817b5454499a92c85888b42dc2ad075e4493a"
  integrity sha512-INDylKH//YIf2w67D+IjkfVnGVrZ/D94DAU/FPPm6T4jEPbEDQvo9r2wTj0ncFdtJH8+V8BggZTaN8Rzk5wkgw==

errno@^0.1.1:
  version "0.1.8"
  resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
  integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
  dependencies:
    prr "~1.0.1"

esbuild-android-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be"
  integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==

esbuild-android-arm64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771"
  integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==

esbuild-darwin-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz#24ba67b9a8cb890a3c08d9018f887cc221cdda25"
  integrity sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==

esbuild-darwin-arm64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73"
  integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==

esbuild-freebsd-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d"
  integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==

esbuild-freebsd-arm64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48"
  integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==

esbuild-linux-32@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5"
  integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==

esbuild-linux-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652"
  integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==

esbuild-linux-arm64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b"
  integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==

esbuild-linux-arm@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59"
  integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==

esbuild-linux-mips64le@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34"
  integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==

esbuild-linux-ppc64le@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e"
  integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==

esbuild-linux-riscv64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8"
  integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==

esbuild-linux-s390x@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6"
  integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==

esbuild-netbsd-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81"
  integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==

esbuild-openbsd-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b"
  integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==

esbuild-sunos-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da"
  integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==

esbuild-windows-32@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31"
  integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==

esbuild-windows-64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4"
  integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==

esbuild-windows-arm64@0.14.54:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982"
  integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==

esbuild@^0.14.47:
  version "0.14.54"
  resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.54.tgz#8b44dcf2b0f1a66fc22459943dccf477535e9aa2"
  integrity sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==
  optionalDependencies:
    "@esbuild/linux-loong64" "0.14.54"
    esbuild-android-64 "0.14.54"
    esbuild-android-arm64 "0.14.54"
    esbuild-darwin-64 "0.14.54"
    esbuild-darwin-arm64 "0.14.54"
    esbuild-freebsd-64 "0.14.54"
    esbuild-freebsd-arm64 "0.14.54"
    esbuild-linux-32 "0.14.54"
    esbuild-linux-64 "0.14.54"
    esbuild-linux-arm "0.14.54"
    esbuild-linux-arm64 "0.14.54"
    esbuild-linux-mips64le "0.14.54"
    esbuild-linux-ppc64le "0.14.54"
    esbuild-linux-riscv64 "0.14.54"
    esbuild-linux-s390x "0.14.54"
    esbuild-netbsd-64 "0.14.54"
    esbuild-openbsd-64 "0.14.54"
    esbuild-sunos-64 "0.14.54"
    esbuild-windows-32 "0.14.54"
    esbuild-windows-64 "0.14.54"
    esbuild-windows-arm64 "0.14.54"

escalade@^3.1.1:
  version "3.1.1"
  resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
  integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==

escape-string-regexp@^1.0.5:
  version "1.0.5"
  resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
  integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==

fast-deep-equal@^3.1.3:
  version "3.1.3"
  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==

fast-glob@^3.2.11:
  version "3.2.11"
  resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
  integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
  dependencies:
    "@nodelib/fs.stat" "^2.0.2"
    "@nodelib/fs.walk" "^1.2.3"
    glob-parent "^5.1.2"
    merge2 "^1.3.0"
    micromatch "^4.0.4"

fastq@^1.6.0:
  version "1.13.0"
  resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
  integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
  dependencies:
    reusify "^1.0.4"

fill-range@^7.0.1:
  version "7.0.1"
  resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
  integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
  dependencies:
    to-regex-range "^5.0.1"

follow-redirects@^1.14.9:
  version "1.15.1"
  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
  integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==

form-data@^4.0.0:
  version "4.0.0"
  resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
  dependencies:
    asynckit "^0.4.0"
    combined-stream "^1.0.8"
    mime-types "^2.1.12"

fraction.js@^4.2.0:
  version "4.2.0"
  resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
  integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==

fs.realpath@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
  integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==

fsevents@~2.3.2:
  version "2.3.2"
  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
  integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==

function-bind@^1.1.1:
  version "1.1.1"
  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==

gensync@^1.0.0-beta.2:
  version "1.0.0-beta.2"
  resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
  integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==

get-nonce@^1.0.0:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/get-nonce/-/get-nonce-1.0.1.tgz#fdf3f0278073820d2ce9426c18f07481b1e0cdf3"
  integrity sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==

glob-parent@^5.1.2, glob-parent@~5.1.2:
  version "5.1.2"
  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
  integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
  dependencies:
    is-glob "^4.0.1"

glob-parent@^6.0.2:
  version "6.0.2"
  resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
  integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
  dependencies:
    is-glob "^4.0.3"

glob-regex@^0.3.0:
  version "0.3.2"
  resolved "https://registry.yarnpkg.com/glob-regex/-/glob-regex-0.3.2.tgz#27348f2f60648ec32a4a53137090b9fb934f3425"
  integrity sha512-m5blUd3/OqDTWwzBBtWBPrGlAzatRywHameHeekAZyZrskYouOGdNB8T/q6JucucvJXtOuyHIn0/Yia7iDasDw==

glob@^7.1.3:
  version "7.2.3"
  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
  dependencies:
    fs.realpath "^1.0.0"
    inflight "^1.0.4"
    inherits "2"
    minimatch "^3.1.1"
    once "^1.3.0"
    path-is-absolute "^1.0.0"

globals@^11.1.0:
  version "11.12.0"
  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==

globrex@^0.1.2:
  version "0.1.2"
  resolved "https://registry.yarnpkg.com/globrex/-/globrex-0.1.2.tgz#dd5d9ec826232730cd6793a5e33a9302985e6098"
  integrity sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==

graceful-fs@^4.1.2:
  version "4.2.10"
  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
  integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==

has-flag@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
  integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==

has-flag@^4.0.0:
  version "4.0.0"
  resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
  integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==

has@^1.0.3:
  version "1.0.3"
  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
  dependencies:
    function-bind "^1.1.1"

history@^5.2.0:
  version "5.3.0"
  resolved "https://registry.yarnpkg.com/history/-/history-5.3.0.tgz#1548abaa245ba47992f063a0783db91ef201c73b"
  integrity sha512-ZqaKwjjrAYUYfLG+htGaIIZ4nioX2L70ZUMIFysS3xvBsSG4x/n1V6TXV3N8ZYNuFGlDirFg32T7B6WOUPDYcQ==
  dependencies:
    "@babel/runtime" "^7.7.6"

iconv-lite@^0.6.3:
  version "0.6.3"
  resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
  integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
  dependencies:
    safer-buffer ">= 2.1.2 < 3.0.0"

image-size@~0.5.0:
  version "0.5.5"
  resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
  integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==

inflight@^1.0.4:
  version "1.0.6"
  resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
  integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
  dependencies:
    once "^1.3.0"
    wrappy "1"

inherits@2:
  version "2.0.4"
  resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==

invariant@^2.2.4:
  version "2.2.4"
  resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
  integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
  dependencies:
    loose-envify "^1.0.0"

is-binary-path@~2.1.0:
  version "2.1.0"
  resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
  integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
  dependencies:
    binary-extensions "^2.0.0"

is-core-module@^2.9.0:
  version "2.10.0"
  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed"
  integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==
  dependencies:
    has "^1.0.3"

is-extglob@^2.1.1:
  version "2.1.1"
  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==

is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
  version "4.0.3"
  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
  integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
  dependencies:
    is-extglob "^2.1.1"

is-number@^7.0.0:
  version "7.0.0"
  resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
  integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==

is-what@^3.14.1:
  version "3.14.1"
  resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
  integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==

js-sha3@0.8.0:
  version "0.8.0"
  resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
  integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==

"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
  version "4.0.0"
  resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
  integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==

jsesc@^2.5.1:
  version "2.5.2"
  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==

json2mq@^0.2.0:
  version "0.2.0"
  resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
  integrity sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==
  dependencies:
    string-convert "^0.2.0"

json5@^2.2.1:
  version "2.2.1"
  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
  integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==

klona@^2.0.4:
  version "2.0.5"
  resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc"
  integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==

less-loader@11.0.0:
  version "11.0.0"
  resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-11.0.0.tgz#a31b2bc5cdfb62f1c7de9b2d01cd944c22b1a024"
  integrity sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==
  dependencies:
    klona "^2.0.4"

less@4.1.3:
  version "4.1.3"
  resolved "https://registry.yarnpkg.com/less/-/less-4.1.3.tgz#175be9ddcbf9b250173e0a00b4d6920a5b770246"
  integrity sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==
  dependencies:
    copy-anything "^2.0.1"
    parse-node-version "^1.0.1"
    tslib "^2.3.0"
  optionalDependencies:
    errno "^0.1.1"
    graceful-fs "^4.1.2"
    image-size "~0.5.0"
    make-dir "^2.1.0"
    mime "^1.4.1"
    needle "^3.1.0"
    source-map "~0.6.0"

lilconfig@^2.0.5, lilconfig@^2.0.6:
  version "2.0.6"
  resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4"
  integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==

lodash.isequal@^4.5.0:
  version "4.5.0"
  resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
  integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==

lodash.merge@^4.6.2:
  version "4.6.2"
  resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
  integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==

lodash.tonumber@^4.0.3:
  version "4.0.3"
  resolved "https://registry.yarnpkg.com/lodash.tonumber/-/lodash.tonumber-4.0.3.tgz#0b96b31b35672793eb7f5a63ee791f1b9e9025d9"
  integrity sha512-SY0SwuPOHRwKcCNTdsntPYb+Zddz5mDUIVFABzRMqmAiL41pMeyoQFGxYAw5zdc9NnH4pbJqiqqp5ckfxa+zSA==

lodash@^4.0.1, lodash@^4.17.21:
  version "4.17.21"
  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
  version "1.4.0"
  resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
  integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
  dependencies:
    js-tokens "^3.0.0 || ^4.0.0"

lower-case@^2.0.2:
  version "2.0.2"
  resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
  integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
  dependencies:
    tslib "^2.0.3"

magic-string@^0.26.2:
  version "0.26.2"
  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.2.tgz#5331700e4158cd6befda738bb6b0c7b93c0d4432"
  integrity sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==
  dependencies:
    sourcemap-codec "^1.4.8"

make-dir@^2.1.0:
  version "2.1.0"
  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5"
  integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==
  dependencies:
    pify "^4.0.1"
    semver "^5.6.0"

match-sorter@^4.1.0:
  version "4.2.1"
  resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-4.2.1.tgz#575b4b3737185ba9518b67612b66877ea0b37358"
  integrity sha512-s+3h9TiZU9U1pWhIERHf8/f4LmBN6IXaRgo2CI17+XGByGS1GvG5VvXK9pcGyCjGe3WM3mSYRC3ipGrd5UEVgw==
  dependencies:
    "@babel/runtime" "^7.10.5"
    remove-accents "0.4.2"

match-sorter@^6.0.2:
  version "6.3.1"
  resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
  integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
  dependencies:
    "@babel/runtime" "^7.12.5"
    remove-accents "0.4.2"

memoize-one@^5.1.1:
  version "5.2.1"
  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
  integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==

memoize-one@^6.0.0:
  version "6.0.0"
  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
  integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==

merge2@^1.3.0:
  version "1.4.1"
  resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
  integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==

micromatch@^4.0.4:
  version "4.0.5"
  resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
  integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
  dependencies:
    braces "^3.0.2"
    picomatch "^2.3.1"

microseconds@0.2.0:
  version "0.2.0"
  resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
  integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==

mime-db@1.52.0:
  version "1.52.0"
  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==

mime-types@^2.1.12:
  version "2.1.35"
  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
  dependencies:
    mime-db "1.52.0"

mime@^1.4.1:
  version "1.6.0"
  resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==

minimatch@^3.1.1:
  version "3.1.2"
  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
  dependencies:
    brace-expansion "^1.1.7"

minimist@^1.2.6:
  version "1.2.6"
  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
  integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==

moment@^2.24.0, moment@^2.27.0, moment@^2.29.2:
  version "2.29.4"
  resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
  integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==

ms@2.1.2:
  version "2.1.2"
  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
  integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

ms@^2.1.1:
  version "2.1.3"
  resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==

nano-time@1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
  integrity sha512-flnngywOoQ0lLQOTRNexn2gGSNuM9bKj9RZAWSzhQ+UJYaAFG9bac4DW9VHjUAzrOaIcajHybCTHe/bkvozQqA==
  dependencies:
    big-integer "^1.6.16"

nanoid@^3.3.4:
  version "3.3.4"
  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
  integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==

needle@^3.1.0:
  version "3.1.0"
  resolved "https://registry.yarnpkg.com/needle/-/needle-3.1.0.tgz#3bf5cd090c28eb15644181ab6699e027bd6c53c9"
  integrity sha512-gCE9weDhjVGCRqS8dwDR/D3GTAeyXLXuqp7I8EzH6DllZGXSUyxuqqLh+YX9rMAWaaTFyVAg6rHGL25dqvczKw==
  dependencies:
    debug "^3.2.6"
    iconv-lite "^0.6.3"
    sax "^1.2.4"

no-case@^3.0.4:
  version "3.0.4"
  resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
  integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
  dependencies:
    lower-case "^2.0.2"
    tslib "^2.0.3"

node-releases@^2.0.6:
  version "2.0.6"
  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
  integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==

normalize-path@^3.0.0, normalize-path@~3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
  integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==

normalize-range@^0.1.2:
  version "0.1.2"
  resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
  integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==

object-assign@4.x, object-assign@^4.1.1:
  version "4.1.1"
  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
  integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==

object-hash@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
  integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==

oblivious-set@1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
  integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==

omit.js@^2.0.2:
  version "2.0.2"
  resolved "https://registry.yarnpkg.com/omit.js/-/omit.js-2.0.2.tgz#dd9b8436fab947a5f3ff214cb2538631e313ec2f"
  integrity sha512-hJmu9D+bNB40YpL9jYebQl4lsTW6yEHRTroJzNLqQJYHm7c+NQnJGfZmIWh8S3q3KoaxV1aLhV6B3+0N0/kyJg==

once@^1.3.0:
  version "1.4.0"
  resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
  dependencies:
    wrappy "1"

param-case@^3.0.4:
  version "3.0.4"
  resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
  integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
  dependencies:
    dot-case "^3.0.4"
    tslib "^2.0.3"

parse-node-version@^1.0.1:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b"
  integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==

pascal-case@^3.1.2:
  version "3.1.2"
  resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
  integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
  dependencies:
    no-case "^3.0.4"
    tslib "^2.0.3"

path-is-absolute@^1.0.0:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==

path-parse@^1.0.7:
  version "1.0.7"
  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
  integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==

path-to-regexp@2.4.0:
  version "2.4.0"
  resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704"
  integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w==

picocolors@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==

picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
  version "2.3.1"
  resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
  integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==

pify@^2.3.0:
  version "2.3.0"
  resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
  integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==

pify@^4.0.1:
  version "4.0.1"
  resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231"
  integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==

postcss-import@^14.1.0:
  version "14.1.0"
  resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0"
  integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==
  dependencies:
    postcss-value-parser "^4.0.0"
    read-cache "^1.0.0"
    resolve "^1.1.7"

postcss-js@^4.0.0:
  version "4.0.0"
  resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00"
  integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==
  dependencies:
    camelcase-css "^2.0.1"

postcss-load-config@^3.1.4:
  version "3.1.4"
  resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855"
  integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
  dependencies:
    lilconfig "^2.0.5"
    yaml "^1.10.2"

postcss-nested@5.0.6:
  version "5.0.6"
  resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.6.tgz#466343f7fc8d3d46af3e7dba3fcd47d052a945bc"
  integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==
  dependencies:
    postcss-selector-parser "^6.0.6"

postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6:
  version "6.0.10"
  resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
  integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
  dependencies:
    cssesc "^3.0.0"
    util-deprecate "^1.0.2"

postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
  version "4.2.0"
  resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==

postcss@8.4.16, postcss@^8.4.14, postcss@^8.4.16:
  version "8.4.16"
  resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c"
  integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
  dependencies:
    nanoid "^3.3.4"
    picocolors "^1.0.0"
    source-map-js "^1.0.2"

prop-types@^15.5.10, prop-types@^15.5.7:
  version "15.8.1"
  resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
  integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
  dependencies:
    loose-envify "^1.4.0"
    object-assign "^4.1.1"
    react-is "^16.13.1"

prr@~1.0.1:
  version "1.0.1"
  resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
  integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==

queue-microtask@^1.2.2:
  version "1.2.3"
  resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
  integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==

quick-lru@^5.1.1:
  version "5.1.1"
  resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
  integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==

rc-align@^4.0.0:
  version "4.0.12"
  resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-4.0.12.tgz#065b5c68a1cc92a00800c9239320d9fdf5f16207"
  integrity sha512-3DuwSJp8iC/dgHzwreOQl52soj40LchlfUHtgACOUtwGuoFIOVh6n/sCpfqCU8kO5+iz6qR0YKvjgB8iPdE3aQ==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    dom-align "^1.7.0"
    lodash "^4.17.21"
    rc-util "^5.3.0"
    resize-observer-polyfill "^1.5.1"

rc-cascader@~3.6.0:
  version "3.6.1"
  resolved "https://registry.yarnpkg.com/rc-cascader/-/rc-cascader-3.6.1.tgz#2e94fb3ed770ffd71d87ebcf17a9b44a6442e76f"
  integrity sha512-+GmN2Z0IybKT45t0Z94jkjmsOHGxAliobR2tzt05/Gw0AKBYLHX5bdvsVXR7abPnarYyYzZ/cWe8CoFgDjAFNw==
  dependencies:
    "@babel/runtime" "^7.12.5"
    array-tree-filter "^2.1.0"
    classnames "^2.3.1"
    rc-select "~14.1.0"
    rc-tree "~5.6.3"
    rc-util "^5.6.1"

rc-checkbox@~2.3.0:
  version "2.3.2"
  resolved "https://registry.yarnpkg.com/rc-checkbox/-/rc-checkbox-2.3.2.tgz#f91b3678c7edb2baa8121c9483c664fa6f0aefc1"
  integrity sha512-afVi1FYiGv1U0JlpNH/UaEXdh6WUJjcWokj/nUN2TgG80bfG+MDdbfHKlLcNNba94mbjy2/SXJ1HDgrOkXGAjg==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.1"

rc-collapse@~3.3.0:
  version "3.3.1"
  resolved "https://registry.yarnpkg.com/rc-collapse/-/rc-collapse-3.3.1.tgz#fc66d4c9cfeaf41e932b2de6da2d454874aee55a"
  integrity sha512-cOJfcSe3R8vocrF8T+PgaHDrgeA1tX+lwfhwSj60NX9QVRidsILIbRNDLD6nAzmcvVC5PWiIRiR4S1OobxdhCg==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    rc-motion "^2.3.4"
    rc-util "^5.2.1"
    shallowequal "^1.1.0"

rc-dialog@~8.9.0:
  version "8.9.0"
  resolved "https://registry.yarnpkg.com/rc-dialog/-/rc-dialog-8.9.0.tgz#04dc39522f0321ed2e06018d4a7e02a4c32bd3ea"
  integrity sha512-Cp0tbJnrvPchJfnwIvOMWmJ4yjX3HWFatO6oBFD1jx8QkgsQCR0p8nUWAKdd3seLJhEC39/v56kZaEjwp9muoQ==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.6"
    rc-motion "^2.3.0"
    rc-util "^5.21.0"

rc-drawer@~5.1.0, rc-drawer@~5.1.0-alpha.1:
  version "5.1.0"
  resolved "https://registry.yarnpkg.com/rc-drawer/-/rc-drawer-5.1.0.tgz#c1b8a46e5c064ba46a16233fbcfb1ccec6a73c10"
  integrity sha512-pU3Tsn99pxGdYowXehzZbdDVE+4lDXSGb7p8vA9mSmr569oc2Izh4Zw5vLKSe/Xxn2p5MSNbLVqD4tz+pK6SOw==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.6"
    rc-motion "^2.6.1"
    rc-util "^5.21.2"

rc-dropdown@~4.0.0:
  version "4.0.1"
  resolved "https://registry.yarnpkg.com/rc-dropdown/-/rc-dropdown-4.0.1.tgz#f65d9d3d89750241057db59d5a75e43cd4576b68"
  integrity sha512-OdpXuOcme1rm45cR0Jzgfl1otzmU4vuBVb+etXM8vcaULGokAKVpKlw8p6xzspG7jGd/XxShvq+N3VNEfk/l5g==
  dependencies:
    "@babel/runtime" "^7.18.3"
    classnames "^2.2.6"
    rc-trigger "^5.3.1"
    rc-util "^5.17.0"

rc-field-form@~1.27.0:
  version "1.27.1"
  resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.27.1.tgz#11d61ccb43679e71fdbbff0d821326202554df84"
  integrity sha512-RShegnwFu6TH8tl2olCxn+B4Wyh5EiQH8c/7wucbkLNyue05YiH5gomUAg1vbZjp71yFKwegClctsEG5CNBWAA==
  dependencies:
    "@babel/runtime" "^7.18.0"
    async-validator "^4.1.0"
    rc-util "^5.8.0"

rc-image@~5.7.0:
  version "5.7.1"
  resolved "https://registry.yarnpkg.com/rc-image/-/rc-image-5.7.1.tgz#678dc014845954c30237808c00c7b12e5f2a0b07"
  integrity sha512-QyMfdhoUfb5W14plqXSisaYwpdstcLYnB0MjX5ccIK2rydQM9sDPuekQWu500DDGR2dBaIF5vx9XbWkNFK17Fg==
  dependencies:
    "@babel/runtime" "^7.11.2"
    classnames "^2.2.6"
    rc-dialog "~8.9.0"
    rc-util "^5.0.6"

rc-input-number@~7.3.5:
  version "7.3.6"
  resolved "https://registry.yarnpkg.com/rc-input-number/-/rc-input-number-7.3.6.tgz#54d66bd3fdaef0abfded4c734a12ac6d9461ebab"
  integrity sha512-Se62oMOBn9HwF/gSag+YtAYyKZsjJzEsqmyAJHAnAvPfjZJOu7dLMlQRwBbTtELbKXM/Y5Fztcq8CW2Y9f49qA==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.5"
    rc-util "^5.23.0"

rc-input@~0.0.1-alpha.5:
  version "0.0.1-alpha.7"
  resolved "https://registry.yarnpkg.com/rc-input/-/rc-input-0.0.1-alpha.7.tgz#53e3f13871275c21d92b51f80b698f389ad45dd3"
  integrity sha512-eozaqpCYWSY5LBMwlHgC01GArkVEP+XlJ84OMvdkwUnJBSv83Yxa15pZpn7vACAj84uDC4xOA2CoFdbLuqB08Q==
  dependencies:
    "@babel/runtime" "^7.11.1"
    classnames "^2.2.1"
    rc-util "^5.18.1"

rc-mentions@~1.9.1:
  version "1.9.1"
  resolved "https://registry.yarnpkg.com/rc-mentions/-/rc-mentions-1.9.1.tgz#769ea05981af917603d1e2815a8f3ecc02fbf9d1"
  integrity sha512-6VBaEkJjYdGGxAygQvE2NaIegAzLJSDodgzpoV+vonUUCZ4A/d8izVlc2JqCYa0Yi15HiDcWyfRgJYI7OKTILg==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.6"
    rc-menu "~9.6.0"
    rc-textarea "^0.3.0"
    rc-trigger "^5.0.4"
    rc-util "^5.22.5"

rc-menu@~9.6.0:
  version "9.6.2"
  resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.6.2.tgz#3d987c90adf520c014a235c7ac7d4575676ae528"
  integrity sha512-sSobfm30OAw6XfVaj+rEkBfalLxbZ3uOsB876MCG6NmwXQGSKRv+arTBnJnJqAmCGh2dMyb/Jf9kTCMpII6RTg==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    rc-motion "^2.4.3"
    rc-overflow "^1.2.0"
    rc-trigger "^5.1.2"
    rc-util "^5.12.0"
    shallowequal "^1.1.0"

rc-menu@~9.6.3:
  version "9.6.3"
  resolved "https://registry.yarnpkg.com/rc-menu/-/rc-menu-9.6.3.tgz#f0373d0391a97db94147106cddffe87b1e4f4e36"
  integrity sha512-KY9QilKWgkJZ0JSpOBgIpQF2wMRRodRxpIMYyIJ3Nd5N6xfVLOxXCxevHcBplt+Ez7MhUF+I03MuAKqWQJLZgw==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    rc-motion "^2.4.3"
    rc-overflow "^1.2.0"
    rc-trigger "^5.1.2"
    rc-util "^5.12.0"
    shallowequal "^1.1.0"

rc-motion@^2.0.0, rc-motion@^2.0.1, rc-motion@^2.2.0, rc-motion@^2.3.0, rc-motion@^2.3.4, rc-motion@^2.4.3, rc-motion@^2.4.4, rc-motion@^2.6.1:
  version "2.6.2"
  resolved "https://registry.yarnpkg.com/rc-motion/-/rc-motion-2.6.2.tgz#3d31f97e41fb8e4f91a4a4189b6a98ac63342869"
  integrity sha512-4w1FaX3dtV749P8GwfS4fYnFG4Rb9pxvCYPc/b2fw1cmlHJWNNgOFIz7ysiD+eOrzJSvnLJWlNQQncpNMXwwpg==
  dependencies:
    "@babel/runtime" "^7.11.1"
    classnames "^2.2.1"
    rc-util "^5.21.0"

rc-notification@~4.6.0:
  version "4.6.0"
  resolved "https://registry.yarnpkg.com/rc-notification/-/rc-notification-4.6.0.tgz#4e76fc2d0568f03cc93ac18c9e20763ebe29fa46"
  integrity sha512-xF3MKgIoynzjQAO4lqsoraiFo3UXNYlBfpHs0VWvwF+4pimen9/H1DYLN2mfRWhHovW6gRpla73m2nmyIqAMZQ==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    rc-motion "^2.2.0"
    rc-util "^5.20.1"

rc-overflow@^1.0.0, rc-overflow@^1.2.0:
  version "1.2.8"
  resolved "https://registry.yarnpkg.com/rc-overflow/-/rc-overflow-1.2.8.tgz#40f140fabc244118543e627cdd1ef750d9481a88"
  integrity sha512-QJ0UItckWPQ37ZL1dMEBAdY1dhfTXFL9k6oTTcyydVwoUNMnMqCGqnRNA98axSr/OeDKqR6DVFyi8eA5RQI/uQ==
  dependencies:
    "@babel/runtime" "^7.11.1"
    classnames "^2.2.1"
    rc-resize-observer "^1.0.0"
    rc-util "^5.19.2"

rc-pagination@~3.1.17:
  version "3.1.17"
  resolved "https://registry.yarnpkg.com/rc-pagination/-/rc-pagination-3.1.17.tgz#91e690aa894806e344cea88ea4a16d244194a1bd"
  integrity sha512-/BQ5UxcBnW28vFAcP2hfh+Xg15W0QZn8TWYwdCApchMH1H0CxiaUUcULP8uXcFM1TygcdKWdt3JqsL9cTAfdkQ==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.1"

rc-picker@~2.6.10:
  version "2.6.10"
  resolved "https://registry.yarnpkg.com/rc-picker/-/rc-picker-2.6.10.tgz#8d0a473c079388bdb2d7358a2a54c7d5095893b4"
  integrity sha512-9wYtw0DFWs9FO92Qh2D76P0iojUr8ZhLOtScUeOit6ks/F+TBLrOC1uze3IOu+u9gbDAjmosNWLKbBzx/Yuv2w==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.1"
    date-fns "2.x"
    dayjs "1.x"
    moment "^2.24.0"
    rc-trigger "^5.0.4"
    rc-util "^5.4.0"
    shallowequal "^1.1.0"

rc-progress@~3.3.2:
  version "3.3.3"
  resolved "https://registry.yarnpkg.com/rc-progress/-/rc-progress-3.3.3.tgz#eb9bffbacab1534f2542f9f6861ce772254362b1"
  integrity sha512-MDVNVHzGanYtRy2KKraEaWeZLri2ZHWIRyaE1a9MQ2MuJ09m+Wxj5cfcaoaR6z5iRpHpA59YeUxAlpML8N4PJw==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.6"
    rc-util "^5.16.1"

rc-rate@~2.9.0:
  version "2.9.2"
  resolved "https://registry.yarnpkg.com/rc-rate/-/rc-rate-2.9.2.tgz#4a58965d1ecf91896ebae01d458b59056df0b4ea"
  integrity sha512-SaiZFyN8pe0Fgphv8t3+kidlej+cq/EALkAJAc3A0w0XcPaH2L1aggM8bhe1u6GAGuQNAoFvTLjw4qLPGRKV5g==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.5"
    rc-util "^5.0.1"

rc-resize-observer@^1.0.0, rc-resize-observer@^1.1.0, rc-resize-observer@^1.2.0:
  version "1.2.0"
  resolved "https://registry.yarnpkg.com/rc-resize-observer/-/rc-resize-observer-1.2.0.tgz#9f46052f81cdf03498be35144cb7c53fd282c4c7"
  integrity sha512-6W+UzT3PyDM0wVCEHfoW3qTHPTvbdSgiA43buiy8PzmeMnfgnDeb9NjdimMXMl3/TcrvvWl5RRVdp+NqcR47pQ==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.1"
    rc-util "^5.15.0"
    resize-observer-polyfill "^1.5.1"

rc-segmented@~2.1.0:
  version "2.1.0"
  resolved "https://registry.yarnpkg.com/rc-segmented/-/rc-segmented-2.1.0.tgz#0e0afe646c1a0e44a0e18785f518c42633ec8efc"
  integrity sha512-hUlonro+pYoZcwrH6Vm56B2ftLfQh046hrwif/VwLIw1j3zGt52p5mREBwmeVzXnSwgnagpOpfafspzs1asjGw==
  dependencies:
    "@babel/runtime" "^7.11.1"
    classnames "^2.2.1"
    rc-motion "^2.4.4"
    rc-util "^5.17.0"

rc-select@~14.1.0, rc-select@~14.1.1:
  version "14.1.9"
  resolved "https://registry.yarnpkg.com/rc-select/-/rc-select-14.1.9.tgz#9c9eceff00920ad8a0a53b77b76afc177ffe5ab4"
  integrity sha512-DK01+Q7oCWr5jVPiEp/BTQ8xCB4rI4LfXzZtSmBWJhOMuibyZD1Vlz/DlVKCUFmtBM4SzG4/SltGHoGlcbCqiw==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    rc-motion "^2.0.1"
    rc-overflow "^1.0.0"
    rc-trigger "^5.0.4"
    rc-util "^5.16.1"
    rc-virtual-list "^3.2.0"

rc-slider@~10.0.0:
  version "10.0.1"
  resolved "https://registry.yarnpkg.com/rc-slider/-/rc-slider-10.0.1.tgz#7058c68ff1e1aa4e7c3536e5e10128bdbccb87f9"
  integrity sha512-igTKF3zBet7oS/3yNiIlmU8KnZ45npmrmHlUUio8PNbIhzMcsh+oE/r2UD42Y6YD2D/s+kzCQkzQrPD6RY435Q==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.5"
    rc-util "^5.18.1"
    shallowequal "^1.1.0"

rc-steps@~4.1.0:
  version "4.1.4"
  resolved "https://registry.yarnpkg.com/rc-steps/-/rc-steps-4.1.4.tgz#0ba82db202d59ca52d0693dc9880dd145b19dc23"
  integrity sha512-qoCqKZWSpkh/b03ASGx1WhpKnuZcRWmvuW+ZUu4mvMdfvFzVxblTwUM+9aBd0mlEUFmt6GW8FXhMpHkK3Uzp3w==
  dependencies:
    "@babel/runtime" "^7.10.2"
    classnames "^2.2.3"
    rc-util "^5.0.1"

rc-switch@~3.2.0:
  version "3.2.2"
  resolved "https://registry.yarnpkg.com/rc-switch/-/rc-switch-3.2.2.tgz#d001f77f12664d52595b4f6fb425dd9e66fba8e8"
  integrity sha512-+gUJClsZZzvAHGy1vZfnwySxj+MjLlGRyXKXScrtCTcmiYNPzxDFOxdQ/3pK1Kt/0POvwJ/6ALOR8gwdXGhs+A==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.1"
    rc-util "^5.0.1"

rc-table@~7.25.3:
  version "7.25.3"
  resolved "https://registry.yarnpkg.com/rc-table/-/rc-table-7.25.3.tgz#a2941d4fde4c181e687e97a294faca8e4122e26d"
  integrity sha512-McsLJ2rg8EEpRBRYN4Pf9gT7ZNYnjvF9zrBpUBBbUX/fxk+eGi5ff1iPIhMyiHsH71/BmTUzX9nc9XqupD0nMg==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.5"
    rc-resize-observer "^1.1.0"
    rc-util "^5.22.5"
    shallowequal "^1.1.0"

rc-tabs@~11.16.0:
  version "11.16.1"
  resolved "https://registry.yarnpkg.com/rc-tabs/-/rc-tabs-11.16.1.tgz#7c57b6a092d9d0e2df54413b0319f195c27214a9"
  integrity sha512-bR7Dap23YyfzZQwtKomhiFEFzZuE7WaKWo+ypNRSGB9PDKSc6tM12VP8LWYkvmmQHthgwP0WRN8nFbSJWuqLYw==
  dependencies:
    "@babel/runtime" "^7.11.2"
    classnames "2.x"
    rc-dropdown "~4.0.0"
    rc-menu "~9.6.0"
    rc-resize-observer "^1.0.0"
    rc-util "^5.5.0"

rc-textarea@^0.3.0, rc-textarea@~0.3.0:
  version "0.3.7"
  resolved "https://registry.yarnpkg.com/rc-textarea/-/rc-textarea-0.3.7.tgz#987142891efdedb774883c07e2f51b318fde5a11"
  integrity sha512-yCdZ6binKmAQB13hc/oehh0E/QRwoPP1pjF21aHBxlgXO3RzPF6dUu4LG2R4FZ1zx/fQd2L1faktulrXOM/2rw==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "^2.2.1"
    rc-resize-observer "^1.0.0"
    rc-util "^5.7.0"
    shallowequal "^1.1.0"

rc-tooltip@~5.2.0:
  version "5.2.2"
  resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-5.2.2.tgz#e5cafa8ecebf78108936a0bcb93c150fa81ac93b"
  integrity sha512-jtQzU/18S6EI3lhSGoDYhPqNpWajMtS5VV/ld1LwyfrDByQpYmw/LW6U7oFXXLukjfDHQ7Ju705A82PRNFWYhg==
  dependencies:
    "@babel/runtime" "^7.11.2"
    classnames "^2.3.1"
    rc-trigger "^5.0.0"

rc-tree-select@~5.4.0:
  version "5.4.0"
  resolved "https://registry.yarnpkg.com/rc-tree-select/-/rc-tree-select-5.4.0.tgz#c94b961aca68689f5ee3a43e33881cf693d195ef"
  integrity sha512-reRbOqC7Ic/nQocJAJeCl4n6nJUY3NoqiwRXKvhjgZJU7NGr9vIccXEsY+Lghkw5UMpPoxGsIJB0jiAvM18XYA==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    rc-select "~14.1.0"
    rc-tree "~5.6.1"
    rc-util "^5.16.1"

rc-tree@~5.6.1, rc-tree@~5.6.3, rc-tree@~5.6.5:
  version "5.6.6"
  resolved "https://registry.yarnpkg.com/rc-tree/-/rc-tree-5.6.6.tgz#c04253d8f8345ec52fc196dec2be06c7e708125b"
  integrity sha512-HI/q4D4AHOp48OZcBUvJFWkI5OfnZivvGYI0xzI0dy0Mita2KcTGZv7/Yl6Aq3bL3od3x5AqAXq/7qxR3x4Kkg==
  dependencies:
    "@babel/runtime" "^7.10.1"
    classnames "2.x"
    rc-motion "^2.0.1"
    rc-util "^5.16.1"
    rc-virtual-list "^3.4.8"

rc-trigger@^5.0.0, rc-trigger@^5.0.4, rc-trigger@^5.1.2, rc-trigger@^5.2.10, rc-trigger@^5.3.1:
  version "5.3.1"
  resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-5.3.1.tgz#acafadf3eaf384e7f466c303bfa0f34c8137d7b8"
  integrity sha512-5gaFbDkYSefZ14j2AdzucXzlWgU2ri5uEjkHvsf1ynRhdJbKxNOnw4PBZ9+FVULNGFiDzzlVF8RJnR9P/xrnKQ==
  dependencies:
    "@babel/runtime" "^7.18.3"
    classnames "^2.2.6"
    rc-align "^4.0.0"
    rc-motion "^2.0.0"
    rc-util "^5.19.2"

rc-upload@~4.3.0:
  version "4.3.4"
  resolved "https://registry.yarnpkg.com/rc-upload/-/rc-upload-4.3.4.tgz#83ff7d3867631c37adbfd72ea3d1fd7e97ca84af"
  integrity sha512-uVbtHFGNjHG/RyAfm9fluXB6pvArAGyAx8z7XzXXyorEgVIWj6mOlriuDm0XowDHYz4ycNK0nE0oP3cbFnzxiQ==
  dependencies:
    "@babel/runtime" "^7.18.3"
    classnames "^2.2.5"
    rc-util "^5.2.0"

rc-util@^4.19.0:
  version "4.21.1"
  resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.21.1.tgz#88602d0c3185020aa1053d9a1e70eac161becb05"
  integrity sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg==
  dependencies:
    add-dom-event-listener "^1.1.0"
    prop-types "^15.5.10"
    react-is "^16.12.0"
    react-lifecycles-compat "^3.0.4"
    shallowequal "^1.1.0"

rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.12.0, rc-util@^5.15.0, rc-util@^5.16.1, rc-util@^5.17.0, rc-util@^5.18.1, rc-util@^5.19.2, rc-util@^5.2.0, rc-util@^5.2.1, rc-util@^5.20.1, rc-util@^5.21.0, rc-util@^5.21.2, rc-util@^5.22.5, rc-util@^5.23.0, rc-util@^5.3.0, rc-util@^5.4.0, rc-util@^5.5.0, rc-util@^5.6.1, rc-util@^5.7.0, rc-util@^5.8.0, rc-util@^5.9.4:
  version "5.23.0"
  resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.23.0.tgz#a583b1ec3e1832a80eced7a700a494af0b590743"
  integrity sha512-lgm6diJ/pLgyfoZY59Vz7sW4mSoQCgozqbBye9IJ7/mb5w5h4T7h+i2JpXAx/UBQxscBZe68q0sP7EW+qfkKUg==
  dependencies:
    "@babel/runtime" "^7.18.3"
    react-is "^16.12.0"
    shallowequal "^1.1.0"

rc-virtual-list@^3.2.0, rc-virtual-list@^3.4.8:
  version "3.4.8"
  resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.4.8.tgz#c24c10c6940546b7e2a5e9809402c6716adfd26c"
  integrity sha512-qSN+Rv4i/E7RCTvTMr1uZo7f3crJJg/5DekoCagydo9zsXrxj07zsFSxqizqW+ldGA16lwa8So/bIbV9Ofjddg==
  dependencies:
    classnames "^2.2.6"
    rc-resize-observer "^1.0.0"
    rc-util "^5.15.0"

react-dom@18.2.0:
  version "18.2.0"
  resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
  integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
  dependencies:
    loose-envify "^1.1.0"
    scheduler "^0.23.0"

react-is@^16.12.0, react-is@^16.13.1:
  version "16.13.1"
  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
  integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==

react-lifecycles-compat@^3.0.4:
  version "3.0.4"
  resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
  integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==

react-query-devtools@2.6.3:
  version "2.6.3"
  resolved "https://registry.yarnpkg.com/react-query-devtools/-/react-query-devtools-2.6.3.tgz#f7c982839d4b0001cee4d5ddfa826493c2f6341f"
  integrity sha512-pSvWq5Q8zgIP7QbF0+4BerCHLaLn5HPzce7sIXYqz4XEizcYJHkJtcrAwn6bUkCu5JmAt1Y7fViQtZwOIG2SYA==
  dependencies:
    match-sorter "^4.1.0"

react-query@3.39.2:
  version "3.39.2"
  resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.39.2.tgz#9224140f0296f01e9664b78ed6e4f69a0cc9216f"
  integrity sha512-F6hYDKyNgDQfQOuR1Rsp3VRzJnWHx6aRnnIZHMNGGgbL3SBgpZTDg8MQwmxOgpCAoqZJA+JSNCydF1xGJqKOCA==
  dependencies:
    "@babel/runtime" "^7.5.5"
    broadcast-channel "^3.4.1"
    match-sorter "^6.0.2"

react-refresh@^0.14.0:
  version "0.14.0"
  resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
  integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==

react-remove-scroll-bar@^2.3.3:
  version "2.3.3"
  resolved "https://registry.yarnpkg.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.3.tgz#e291f71b1bb30f5f67f023765b7435f4b2b2cd94"
  integrity sha512-i9GMNWwpz8XpUpQ6QlevUtFjHGqnPG4Hxs+wlIJntu/xcsZVEpJcIV71K3ZkqNy2q3GfgvkD7y6t/Sv8ofYSbw==
  dependencies:
    react-style-singleton "^2.2.1"
    tslib "^2.0.0"

react-remove-scroll@2.5.4:
  version "2.5.4"
  resolved "https://registry.yarnpkg.com/react-remove-scroll/-/react-remove-scroll-2.5.4.tgz#afe6491acabde26f628f844b67647645488d2ea0"
  integrity sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==
  dependencies:
    react-remove-scroll-bar "^2.3.3"
    react-style-singleton "^2.2.1"
    tslib "^2.1.0"
    use-callback-ref "^1.3.0"
    use-sidecar "^1.1.2"

react-router-dom@6.3.0:
  version "6.3.0"
  resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.3.0.tgz#a0216da813454e521905b5fa55e0e5176123f43d"
  integrity sha512-uaJj7LKytRxZNQV8+RbzJWnJ8K2nPsOOEuX7aQstlMZKQT0164C+X2w6bnkqU3sjtLvpd5ojrezAyfZ1+0sStw==
  dependencies:
    history "^5.2.0"
    react-router "6.3.0"

react-router@6.3.0:
  version "6.3.0"
  resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.3.0.tgz#3970cc64b4cb4eae0c1ea5203a80334fdd175557"
  integrity sha512-7Wh1DzVQ+tlFjkeo+ujvjSqSJmkt1+8JO+T5xklPlgrh70y7ogx75ODRW0ThWhY7S+6yEDks8TYrtQe/aoboBQ==
  dependencies:
    history "^5.2.0"

react-sortable-hoc@^2.0.0:
  version "2.0.0"
  resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7"
  integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==
  dependencies:
    "@babel/runtime" "^7.2.0"
    invariant "^2.2.4"
    prop-types "^15.5.7"

react-style-singleton@^2.2.1:
  version "2.2.1"
  resolved "https://registry.yarnpkg.com/react-style-singleton/-/react-style-singleton-2.2.1.tgz#f99e420492b2d8f34d38308ff660b60d0b1205b4"
  integrity sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==
  dependencies:
    get-nonce "^1.0.0"
    invariant "^2.2.4"
    tslib "^2.0.0"

react@18.2.0:
  version "18.2.0"
  resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
  integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
  dependencies:
    loose-envify "^1.1.0"

reactcss@^1.2.3:
  version "1.2.3"
  resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd"
  integrity sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==
  dependencies:
    lodash "^4.0.1"

read-cache@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
  integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==
  dependencies:
    pify "^2.3.0"

readdirp@~3.6.0:
  version "3.6.0"
  resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
  integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
  dependencies:
    picomatch "^2.2.1"

recrawl-sync@^2.0.3:
  version "2.2.2"
  resolved "https://registry.yarnpkg.com/recrawl-sync/-/recrawl-sync-2.2.2.tgz#a5a8664c77267d603d601825af544d6716d69e15"
  integrity sha512-E2sI4F25Fu2nrfV+KsnC7/qfk/spQIYXlonfQoS4rwxeNK5BjxnLPbWiRXHVXPwYBOTWtPX5765kTm/zJiL+LQ==
  dependencies:
    "@cush/relative" "^1.0.0"
    glob-regex "^0.3.0"
    slash "^3.0.0"
    tslib "^1.9.3"

regenerator-runtime@^0.13.4:
  version "0.13.9"
  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
  integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==

remove-accents@0.4.2:
  version "0.4.2"
  resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
  integrity sha512-7pXIJqJOq5tFgG1A2Zxti3Ht8jJF337m4sowbuHsW30ZnkQFnDzy9qBNhgzX8ZLW4+UBcXiiR7SwR6pokHsxiA==

resize-observer-polyfill@^1.5.1:
  version "1.5.1"
  resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
  integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==

resolve@^1.1.7, resolve@^1.22.1:
  version "1.22.1"
  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
  integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
  dependencies:
    is-core-module "^2.9.0"
    path-parse "^1.0.7"
    supports-preserve-symlinks-flag "^1.0.0"

reusify@^1.0.4:
  version "1.0.4"
  resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
  integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==

rimraf@3.0.2:
  version "3.0.2"
  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
  dependencies:
    glob "^7.1.3"

"rollup@>=2.75.6 <2.77.0 || ~2.77.0":
  version "2.77.3"
  resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.77.3.tgz#8f00418d3a2740036e15deb653bed1a90ee0cc12"
  integrity sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==
  optionalDependencies:
    fsevents "~2.3.2"

run-parallel@^1.1.9:
  version "1.2.0"
  resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
  integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
  dependencies:
    queue-microtask "^1.2.2"

safe-buffer@~5.1.1:
  version "5.1.2"
  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==

"safer-buffer@>= 2.1.2 < 3.0.0":
  version "2.1.2"
  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==

sax@^1.2.4:
  version "1.2.4"
  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==

scheduler@^0.23.0:
  version "0.23.0"
  resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
  integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
  dependencies:
    loose-envify "^1.1.0"

scroll-into-view-if-needed@^2.2.25:
  version "2.2.29"
  resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
  integrity sha512-hxpAR6AN+Gh53AdAimHM6C8oTN1ppwVZITihix+WqalywBeFcQ6LdQP5ABNl26nX8GTEL7VT+b8lKpdqq65wXg==
  dependencies:
    compute-scroll-into-view "^1.0.17"

semver@^5.6.0:
  version "5.7.1"
  resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
  integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==

semver@^6.3.0:
  version "6.3.0"
  resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
  integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==

shallowequal@^1.1.0:
  version "1.1.0"
  resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
  integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==

slash@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
  integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==

source-map-js@^1.0.2:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==

source-map@~0.6.0:
  version "0.6.1"
  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==

sourcemap-codec@^1.4.8:
  version "1.4.8"
  resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
  integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==

string-convert@^0.2.0:
  version "0.2.1"
  resolved "https://registry.yarnpkg.com/string-convert/-/string-convert-0.2.1.tgz#6982cc3049fbb4cd85f8b24568b9d9bf39eeff97"
  integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==

strip-bom@^3.0.0:
  version "3.0.0"
  resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
  integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==

supports-color@^5.3.0:
  version "5.5.0"
  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
  integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
  dependencies:
    has-flag "^3.0.0"

supports-color@^7.1.0:
  version "7.2.0"
  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
  integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
  dependencies:
    has-flag "^4.0.0"

supports-preserve-symlinks-flag@^1.0.0:
  version "1.0.0"
  resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
  integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==

swr@^1.2.0:
  version "1.3.0"
  resolved "https://registry.yarnpkg.com/swr/-/swr-1.3.0.tgz#c6531866a35b4db37b38b72c45a63171faf9f4e8"
  integrity sha512-dkghQrOl2ORX9HYrMDtPa7LTVHJjCTeZoB1dqTbnnEDlSvN8JEKpYIYurDfvbQFUUS8Cg8PceFVZNkW0KNNYPw==

tailwindcss@3.1.8:
  version "3.1.8"
  resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.8.tgz#4f8520550d67a835d32f2f4021580f9fddb7b741"
  integrity sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g==
  dependencies:
    arg "^5.0.2"
    chokidar "^3.5.3"
    color-name "^1.1.4"
    detective "^5.2.1"
    didyoumean "^1.2.2"
    dlv "^1.1.3"
    fast-glob "^3.2.11"
    glob-parent "^6.0.2"
    is-glob "^4.0.3"
    lilconfig "^2.0.6"
    normalize-path "^3.0.0"
    object-hash "^3.0.0"
    picocolors "^1.0.0"
    postcss "^8.4.14"
    postcss-import "^14.1.0"
    postcss-js "^4.0.0"
    postcss-load-config "^3.1.4"
    postcss-nested "5.0.6"
    postcss-selector-parser "^6.0.10"
    postcss-value-parser "^4.2.0"
    quick-lru "^5.1.1"
    resolve "^1.22.1"

tinycolor2@^1.4.2:
  version "1.4.2"
  resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.4.2.tgz#3f6a4d1071ad07676d7fa472e1fac40a719d8803"
  integrity sha512-vJhccZPs965sV/L2sU4oRQVAos0pQXwsvTLkWYdqJ+a8Q5kPFzJTuOFwy7UniPli44NKQGAglksjvOcpo95aZA==

to-fast-properties@^2.0.0:
  version "2.0.0"
  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
  integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==

to-regex-range@^5.0.1:
  version "5.0.1"
  resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
  integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
  dependencies:
    is-number "^7.0.0"

toggle-selection@^1.0.6:
  version "1.0.6"
  resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
  integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==

tsconfig-paths@^4.0.0:
  version "4.1.0"
  resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.0.tgz#f8ef7d467f08ae3a695335bf1ece088c5538d2c1"
  integrity sha512-AHx4Euop/dXFC+Vx589alFba8QItjF+8hf8LtmuiCwHyI4rHXQtOOENaM8kvYf5fR0dRChy3wzWIZ9WbB7FWow==
  dependencies:
    json5 "^2.2.1"
    minimist "^1.2.6"
    strip-bom "^3.0.0"

tslib@^1.0.0, tslib@^1.9.3:
  version "1.14.1"
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
  integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==

tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0:
  version "2.4.0"
  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
  integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==

typescript@4.7.4:
  version "4.7.4"
  resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
  integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==

universal-cookie@4.0.4:
  version "4.0.4"
  resolved "https://registry.yarnpkg.com/universal-cookie/-/universal-cookie-4.0.4.tgz#06e8b3625bf9af049569ef97109b4bb226ad798d"
  integrity sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==
  dependencies:
    "@types/cookie" "^0.3.3"
    cookie "^0.4.0"

unload@2.2.0:
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
  integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
  dependencies:
    "@babel/runtime" "^7.6.2"
    detect-node "^2.0.4"

unstated-next@^1.1.0:
  version "1.1.0"
  resolved "https://registry.yarnpkg.com/unstated-next/-/unstated-next-1.1.0.tgz#7bb4911a12fdf3cc8ad3eb11a0b315e4a8685ea8"
  integrity sha512-AAn47ZncPvgBGOvMcn8tSRxsrqwf2VdAPxLASTuLJvZt4rhKfDvUkmYZLGfclImSfTVMv7tF4ynaVxin0JjDCA==

update-browserslist-db@^1.0.5:
  version "1.0.5"
  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38"
  integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==
  dependencies:
    escalade "^3.1.1"
    picocolors "^1.0.0"

use-callback-ref@^1.3.0:
  version "1.3.0"
  resolved "https://registry.yarnpkg.com/use-callback-ref/-/use-callback-ref-1.3.0.tgz#772199899b9c9a50526fedc4993fc7fa1f7e32d5"
  integrity sha512-3FT9PRuRdbB9HfXhEq35u4oZkvpJ5kuYbpqhCfmiZyReuRgpnhDlbr2ZEnnuS0RrJAPn6l23xjFg9kpDM+Ms7w==
  dependencies:
    tslib "^2.0.0"

use-isomorphic-layout-effect@^1.1.1:
  version "1.1.2"
  resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
  integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==

use-json-comparison@^1.0.3, use-json-comparison@^1.0.5:
  version "1.0.6"
  resolved "https://registry.yarnpkg.com/use-json-comparison/-/use-json-comparison-1.0.6.tgz#a012bbc258ce745db1f56745dc653f575226cb21"
  integrity sha512-xPadt5yMRbEmVfOSGFSMqjjICrq7nLbfSH3rYIXsrtcuFX7PmbYDN/ku8ObBn3v8o/yZelO1OxUS5+5TI3+fUw==

use-media-antd-query@^1.1.0:
  version "1.1.0"
  resolved "https://registry.yarnpkg.com/use-media-antd-query/-/use-media-antd-query-1.1.0.tgz#f083ad7e292c1c0261b6bbfaac0edc3e0920d85d"
  integrity sha512-B6kKZwNV4R+l4Rl11sWO7HqOay9alzs1Vp1b4YJqjz33YxbltBCZtt/yxXxkXN9rc1S7OeEL/GbwC30Wmqhw6Q==

use-sidecar@^1.1.2:
  version "1.1.2"
  resolved "https://registry.yarnpkg.com/use-sidecar/-/use-sidecar-1.1.2.tgz#2f43126ba2d7d7e117aa5855e5d8f0276dfe73c2"
  integrity sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==
  dependencies:
    detect-node-es "^1.1.0"
    tslib "^2.0.0"

use-sync-external-store@^1.2.0:
  version "1.2.0"
  resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
  integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==

util-deprecate@^1.0.2:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
  integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==

vite-plugin-imp@2.2.0:
  version "2.2.0"
  resolved "https://registry.yarnpkg.com/vite-plugin-imp/-/vite-plugin-imp-2.2.0.tgz#537628fdce546f6b52356b286aae0a5bfdb96539"
  integrity sha512-L7qHTtgwbv8c05SEXAo7xhHeROW5Su8tWWUud+cFQWWHBixkBAcxqZmGFe/UoH0AhOFVqLVFAh/L+1v7W7TEiQ==
  dependencies:
    "@babel/core" "^7.12.10"
    "@babel/generator" "^7.12.11"
    "@babel/parser" "^7.12.11"
    "@babel/traverse" "^7.12.12"
    chalk "^4.1.0"
    param-case "^3.0.4"
    pascal-case "^3.1.2"

vite-tsconfig-paths@3.5.0:
  version "3.5.0"
  resolved "https://registry.yarnpkg.com/vite-tsconfig-paths/-/vite-tsconfig-paths-3.5.0.tgz#bfdf93f8072eff04125112ea9602fd50ae8cdad9"
  integrity sha512-NKIubr7gXgh/3uniQaOytSg+aKWPrjquP6anAy+zCWEn6h9fB8z2/qdlfQrTgZWaXJ2pHVlllrSdRZltHn9P4g==
  dependencies:
    debug "^4.1.1"
    globrex "^0.1.2"
    recrawl-sync "^2.0.3"
    tsconfig-paths "^4.0.0"

vite@3.0.9:
  version "3.0.9"
  resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.9.tgz#45fac22c2a5290a970f23d66c1aef56a04be8a30"
  integrity sha512-waYABTM+G6DBTCpYAxvevpG50UOlZuynR0ckTK5PawNVt7ebX6X7wNXHaGIO6wYYFXSM7/WcuFuO2QzhBB6aMw==
  dependencies:
    esbuild "^0.14.47"
    postcss "^8.4.16"
    resolve "^1.22.1"
    rollup ">=2.75.6 <2.77.0 || ~2.77.0"
  optionalDependencies:
    fsevents "~2.3.2"

warning@^4.0.3:
  version "4.0.3"
  resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
  integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==
  dependencies:
    loose-envify "^1.0.0"

wrappy@1:
  version "1.0.2"
  resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==

xtend@^4.0.2:
  version "4.0.2"
  resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==

yaml@^1.10.2:
  version "1.10.2"
  resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
  integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==

vite.config.ts METASEP
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import vitePluginImp from "vite-plugin-imp";
import { splitVendorChunkPlugin } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

// https://vitejs.dev/config/
export default defineConfig({
  build: {
    manifest: true,
    outDir: "../static/dist",
    emptyOutDir: true,
    rollupOptions: {
      input: {
        main: "./src/main.tsx",
      },
      output: {
        chunkFileNames: undefined,
      },
    },
  },
  server: {
    host: "localhost",
    port: 3000,
    open: false,
    middlewareMode: false,
  },
  base: process.env.mode === "production" ? "./" : "/static/",
  publicDir: "/public",
  root: "./src",
  resolve: {
    alias: [{ find: /^~/, replacement: "" }],
  },

  plugins: [
    react(),
    splitVendorChunkPlugin(),
    tsconfigPaths(),
    vitePluginImp({
      optimize: true,
      libList: [
        {
          libName: "antd",
          libDirectory: "es",
          style: (name) => `antd/es/${name}/style`,
        },
      ],
    }),
  ],
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true, //
      },
    },
  },
});

tsconfig.node.json METASEP
{
  "compilerOptions": {
    "composite": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "allowSyntheticDefaultImports": true
  },
  "include": ["vite.config.ts"]
}

tsconfig.json METASEP
{
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": false,
    "skipLibCheck": true,
    "esModuleInterop": false,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

tailwind.config.cjs METASEP
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  theme: {
    extend: {
      fontFamily: {
        // add new font family
        main: ["Inter", "sans-serif"],
      },
    },
    colors: {
      // custom color palette
      primary: "#441151",
      secondary: "#EE85B5",
      violet: "#883677",
      congo: "##FF958C",
      success: "#5FC790",
      warning: "#FFA600",
      danger: "#FF5630",
      dark: "#2E3A44",
      info: "#1CA7EC",
      black: "#2E3A44",
      grey1: "#A0AABF",
      grey2: "#C0C6D4",
      grey3: "#E3E8F1",
      light: "#F9FBFC",
      white: "#FFF",
    },
  },
  plugins: [],
};

runtime.txt METASEP
python-3.9.13

requirements.txt METASEP
amqp==5.1.1
arrow==1.2.2
asgiref==3.5.2
astroid==2.11.7
async-timeout==4.0.2
Babel==2.10.3
billiard==3.6.4.0
blessed==1.19.1
celery==5.2.7
certifi==2022.6.15
cffi==1.15.1
cfgv==3.3.1
charset-normalizer==2.1.0
click==8.1.3
click-didyoumean==0.3.0
click-plugins==1.1.1
click-repl==0.2.0
cryptography==37.0.4
Deprecated==1.2.13
dill==0.3.5.1
distlib==0.3.5
dj-database-url==1.0.0
Django==4.0.5
django-appconf==1.0.5
django-cors-headers==3.13.0
django-cryptography==1.1
django-extensions==3.2.0
django-grappelli==3.0.3
django-loginas==0.3.10
django-model-utils==4.2.0
django-money==3.0.0
django-picklefield==3.1
django-q==1.3.9
djangorestframework==3.13.1
djangorestframework-api-key==2.2.0
filelock==3.7.1
gunicorn==20.1.0
identify==2.5.3
idna==3.3
isort==5.10.1
jsonfield==3.1.0
kombu==5.2.4
lazy-object-proxy==1.7.1
mccabe==0.7.0
nodeenv==1.7.0
packaging==21.3
platformdirs==2.5.2
pre-commit==2.20.0
prompt-toolkit==3.0.30
psycopg2==2.9.3
py-moneyed==2.0
pycparser==2.21
pylint==2.14.5
pyparsing==3.0.9
python-dateutil==2.8.2
python-dotenv==0.20.0
pytz==2022.2.1
PyYAML==6.0
redis==3.5.3
requests==2.28.1
sentry-sdk==1.9.2
six==1.16.0
sqlparse==0.4.2
stripe==4.0.2
toml==0.10.2
tomli==2.0.1
tomlkit==0.11.1
typing_extensions==4.3.0
urllib3==1.26.11
vine==5.0.0
virtualenv==20.16.3
wcwidth==0.2.5
whitenoise==6.2.0
wrapt==1.14.1

renovate.json METASEP
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base"
  ]
}

postcss.config.cjs METASEP
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

package.json METASEP
{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "@ant-design/pro-components": "^1.1.16",
    "@radix-ui/react-popover": "1.0.0",
    "@radix-ui/react-toast": "1.0.0",
    "@tanstack/react-query-devtools": "4.2.1",
    "@types/node": "18.7.3",
    "antd": "4.22.4",
    "axios": "0.27.2",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-query": "3.39.2",
    "react-query-devtools": "2.6.3",
    "react-router-dom": "6.3.0",
    "universal-cookie": "4.0.4",
    "vite-tsconfig-paths": "3.5.0"
  },
  "devDependencies": {
    "@types/react": "18.0.17",
    "@types/react-dom": "18.0.6",
    "@vitejs/plugin-react": "2.0.1",
    "autoprefixer": "10.4.8",
    "less": "4.1.3",
    "less-loader": "11.0.0",
    "postcss": "8.4.16",
    "tailwindcss": "3.1.8",
    "typescript": "4.7.4",
    "vite": "3.0.9",
    "vite-plugin-imp": "2.2.0"
  }
}

dump.rdb METASEP

docker-compose.yaml METASEP
services:
  db:
    image: postgres:14-alpine
    restart: on-failure
    env_file:
      - ./env/.env
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
      - POSTGRES_USER=${POSTGRES_USER:-db_user}
      - POSTGRES_DB=${POSTGRES_NAME:-postgres}
      - POSTGRES_HOST=${POSTGRES_HOST:-db}

  lotus:
    container_name: lotus_django
    env_file:
      - ./env/.env
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./lotus:/lotus
    command: >
      sh -c "python3 manage.py wait_for_db &&
             python3 manage.py migrate &&
             python manage.py collectstatic --no-input &&
             python3 manage.py initadmin &&
             python3 manage.py setup_tasks &&
             python3 manage.py runserver 0.0.0.0:8000
             "
    depends_on:
      - db
  redis:
    image: redis:7-alpine
    command: redis-server
    env_file:
      - ./env/.env

  celery:
    env_file:
      - ./env/.env
    build:
      context: .
    command: celery -A lotus worker -l info
    volumes:
      - ./lotus:/lotus
    depends_on:
      - db
      - redis
      - lotus
  celery-beat:
    env_file:
      - ./env/.env
    restart: on-failure
    build:
      context: .
    command: celery -A lotus beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
    volumes:
      - ./lotus:/lotus
    depends_on:
      - celery
      - redis
      - lotus
      - db
volumes:
  pgdata:

docker-compose.dev.yaml METASEP
services:
  db:
    image: postgres:14-alpine
    restart: on-failure
    env_file:
      - ./env/.env.dev
    environment:
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
      - POSTGRES_USER=${POSTGRES_USER:-db_user}
      - POSTGRES_DB=${POSTGRES_NAME:-postgres}
      - POSTGRES_HOST=${POSTGRES_HOST:-db}

  lotus:
    container_name: lotus_django
    env_file:
      - ./env/.env.dev
    build:
      context: .
    ports:
      - "8000:8000"
    volumes:
      - ./lotus:/lotus
    command: >
      sh -c "python3 manage.py wait_for_db &&
             python3 manage.py migrate &&
             python3 manage.py initadmin &&
             python3 manage.py setup_tasks &&
             python3 manage.py runserver 0.0.0.0:8000
             "
    depends_on:
      - db

  frontend:
    restart: always
    command: yarn dev
    container_name: front
    build:
      context: .
      dockerfile: Dockerfile.frontend
    ports:
      - "3000:3000"
    stdin_open: true
    depends_on:
      - lotus
  redis:
    image: redis:7-alpine
    command: redis-server
    env_file:
      - ./env/.env.dev

  celery:
    env_file:
      - ./env/.env.dev
    build:
      context: .
    command: celery -A lotus worker -l info
    volumes:
      - ./lotus:/lotus
    depends_on:
      - db
      - redis
      - lotus
  celery-beat:
    env_file:
      - ./env/.env.dev
    restart: on-failure
    build:
      context: .
    command: celery -A lotus beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
    volumes:
      - ./lotus:/lotus
    depends_on:
      - celery
      - redis
      - lotus
      - db
volumes:
  pgdata:

build.sh METASEP
python manage.py migrate
python manage.py initadmin
python manage.py setup_tasks
app.json METASEP
{
  "name": "Lotus",
  "description": "Open Source Metering, Billing and Pricing",
  "website": "https://www.uselotus.io",
  "repository": "https://github.com/uselotus/lotus",
  "logo": "https://uploads-ssl.webflow.com/62e1ba3ad04d5942990d1f50/62e1bb93f43aa65fce59c887_Lotus-Brandmark-Logo-RGB-Black-Large%20(1).svg",
  "success_url": "/admin",
  "addons":[
    {
        "plan":"heroku-postgresql"
    }
  ],
  "buildpacks": [
    {"url": "heroku/nodejs"},
    {"url": "heroku/python"}
  ],
  "env": {
    "SECRET_KEY": {
      "generator": "secret"
    },
    "CELERY_BROKER_URL": {
      "value": "redis://redis:6379/"
    },
    "CELERY_RESULT_BACKEND": {
      "value": "redis://redis:6379/"
    },
    "DJANGO_SUPERUSER_PASSWORD":{
      "value": "password"
    },
    "DJANGO_SUPERUSER_USERNAME":{
      "value": "admin"
    },
    "DJANGO_SUPERUSER_EMAIL":{
      "value": "example@example.com"
    },
    "STRIPE_API_KEY":{
      "value": "123"
    },
    "STRIPE_SECRET_KEY":{
      "value": "1234"
    }
  },
  "scripts":{
    "postdeploy": ""
  }
}
README.md METASEP
<!-- Improved compatibility of back to top link: See: https://github.com/othneildrew/Best-README-Template/pull/73 -->
<a name="readme-top"></a>
<!--



<!-- PROJECT SHIELDS -->
<!--
*** I'm using markdown "reference style" links for readability.
*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
*** See the bottom of this document for the declaration of the reference variables
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
-->
<!-- [![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]
[![LinkedIn][linkedin-shield]][linkedin-url] -->



<!-- PROJECT LOGO -->
![Lotus Logo](./design_resources/Lotus-Horizontal-Logo-RGB-Black-Medium.jpg#gh-dark-mode-only)
![Lotus Logo](./design_resources/Lotus-Horizontal-Logo-RGB-Black-Medium.svg#gh-light-mode-only)

# Lotus: Pricing and Billing On Any Metric

<br/>

Lotus is the open-core pricing and billing engine. We enable SaaS companies to manage and experiment in real-time with custom subscription and usage-based billing.

<br/>

We provide a flexible and modular approach to every step of the billing experience, from metering to invoicing to helping you understand the optimal pricing scheme for your product.

<br/>

[Website](https://www.uselotus.io/) · [Issues](https://github.com/uselotus/lotus/issues) · [Docs](https://docs.uselotus.io/docs/lotus-docs) · [Contact Us](founders@uselotus.io)

<br/>

<!-- GETTING STARTED -->
## Getting Started

There's a few ways to use Lotus. After you set it up, head over to the [Docs](https://docs.uselotus.io/docs/lotus-docs) to learn how to use Lotus!

### :cloud: Cloud Version

Best for convenience and fast deployment. 

Sign up for our `alpha` [here](https://dsl2wm77apy.typeform.com/to/pehx2YSQ?typeform-source=www.uselotus.io) or email us at founders@uselotus.io for more details. 

### :computer: Self-Hosted Version

Best if you want to keep your data local or want full control and extensibility.

#### One-click Deploy with Heroku
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)

#### Set up locally

1. Clone the repo and navigate to the project
   ```sh
   git clone https://github.com/uselotus/lotus.git && cd lotus
   ```
2. Use [pipenv](https://pipenv.pypa.io/en/latest/) to install the requried packages. 
   ```sh
   pipenv sync
   ```
3. Create the necessary environment variables by following [this guide in our docs](https://uselotus.stoplight.io/docs/lotus-docs/branches/main/ylqsg3i42dd5z-docker-self-host-env).
4. Run the project! 
   ```sh
   python manage.py runserver
   ```
You should now be able to access the homepage at [localhost:8000/](http://localhost:8000/), and sign in using the `DJANGO_SUPERUSER_USERNAME` and `DJANGO_SUPERUSER_PASSWORD` you defined.

<p align="right">(<a href="#lotus-pricing-and-billing-your-way">back to top</a>)</p>

<!-- USAGE EXAMPLES -->
## Examples

_For more examples, please refer to the [Documentation](https://uselotus.stoplight.io/)_

<p align="right">(<a href="#lotus-pricing-and-billing-your-way">back to top</a>)</p>

<!-- CONTRIBUTING -->
## Contributing

Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.

If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply [request a feature]().
Don't forget to give the project a star! Thanks again!

<p align="right">(<a href="#lotus-pricing-and-billing-your-way">back to top</a>)</p>

<!-- ABOUT THE PROJECT -->
## About The Project

Lotus is the quickest way to start billing flexibly and experiment with pricing. Our founders studied at MIT together and went on to DE Shaw and Citadel before joining forces to allow SaaS companies to price products accurately. Our metering and billing solutions are open source and free for self-hosting. We charge for our cloud version, enterprise support (SSO, advanced permissions), and extra custom features we will add to the code over time.

<p align="right">(<a href="#lotus-pricing-and-billing-your-way">back to top</a>)</p>

## :bar_chart: Repo Activity
![Alt](https://repobeats.axiom.co/api/embed/408c31cc31b6650e1e5c00414ec4a77b0277cf99.svg "Repobeats analytics image")

<p align="right">(<a href="#lotus-pricing-and-billing-your-way">back to top</a>)</p>

<!-- LICENSE -->
## License

Distributed under the MIT License. See `LICENSE` for more information.

<p align="right">(<a href="#lotus-pricing-and-billing-your-way">back to top</a>)</p>

<!-- CONTACT -->
## Get in Touch

Lotus founders - founders@uselotus.io

Or visit www.uselotus.io

<p align="right">(<a href="#lotus-pricing-and-billing-your-way">back to top</a>)</p>



<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[contributors-shield]: https://img.shields.io/github/contributors/uselotus/lotus.svg?style=for-the-badge
[contributors-url]: [https://github.com/uselotus/lotus/graphs/contributors]
[forks-shield]: https://img.shields.io/github/forks/github_username/repo_name.svg?style=for-the-badge
[forks-url]: https://github.com/uselotus/lotus/network/members
[stars-shield]: https://img.shields.io/github/stars/uselotus/lotus.svg?style=for-the-badge
[stars-url]: https://github.com/uselotus/lotus/stargazers
[issues-shield]: https://img.shields.io/github/issues/github_username/repo_name.svg?style=for-the-badge
[issues-url]: https://github.com/github_username/repo_name/issues
[license-shield]: https://img.shields.io/github/license/github_username/repo_name.svg?style=for-the-badge
[license-url]: https://github.com/github_username/repo_name/blob/master/LICENSE.txt
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
[linkedin-url]: https://linkedin.com/in/uselotusio
[product-screenshot]: images/screenshot.png
[Next.js]: https://img.shields.io/badge/next.js-000000?style=for-the-badge&logo=nextdotjs&logoColor=white
[Next-url]: https://nextjs.org/
[React.js]: https://img.shields.io/badge/React-20232A?style=for-the-badge&logo=react&logoColor=61DAFB
[React-url]: https://reactjs.org/
[Vue.js]: https://img.shields.io/badge/Vue.js-35495E?style=for-the-badge&logo=vuedotjs&logoColor=4FC08D
[Vue-url]: https://vuejs.org/
[Angular.io]: https://img.shields.io/badge/Angular-DD0031?style=for-the-badge&logo=angular&logoColor=white
[Angular-url]: https://angular.io/
[Svelte.dev]: https://img.shields.io/badge/Svelte-4A4A55?style=for-the-badge&logo=svelte&logoColor=FF3E00
[Svelte-url]: https://svelte.dev/
[Laravel.com]: https://img.shields.io/badge/Laravel-FF2D20?style=for-the-badge&logo=laravel&logoColor=white
[Laravel-url]: https://laravel.com
[Bootstrap.com]: https://img.shields.io/badge/Bootstrap-563D7C?style=for-the-badge&logo=bootstrap&logoColor=white
[Bootstrap-url]: https://getbootstrap.com
[JQuery.com]: https://img.shields.io/badge/jQuery-0769AD?style=for-the-badge&logo=jquery&logoColor=white
[JQuery-url]: https://jquery.com

Procfile METASEP
release: chmod u+x build.sh && ./build.sh
web: gunicorn lotus.wsgi

Pipfile.lock METASEP
{
    "_meta": {
        "hash": {
            "sha256": "d7d44bd36169914c05386647d3b9f98003df1be899d18f84c7dbf5e968e5c307"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.9"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "amqp": {
            "hashes": [
                "sha256:2c1b13fecc0893e946c65cbd5f36427861cffa4ea2201d8f6fca22e2a373b5e2",
                "sha256:6f0956d2c23d8fa6e7691934d8c3930eadb44972cbbd1a7ae3a520f735d43359"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==5.1.1"
        },
        "arrow": {
            "hashes": [
                "sha256:05caf1fd3d9a11a1135b2b6f09887421153b94558e5ef4d090b567b47173ac2b",
                "sha256:d622c46ca681b5b3e3574fcb60a04e5cc81b9625112d5fb2b44220c36c892177"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==1.2.2"
        },
        "asgiref": {
            "hashes": [
                "sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4",
                "sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==3.5.2"
        },
        "babel": {
            "hashes": [
                "sha256:7614553711ee97490f732126dc077f8d0ae084ebc6a96e23db1482afabdb2c51",
                "sha256:ff56f4892c1c4bf0d814575ea23471c230d544203c7748e8c68f0089478d48eb"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==2.10.3"
        },
        "billiard": {
            "hashes": [
                "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547",
                "sha256:87103ea78fa6ab4d5c751c4909bcff74617d985de7fa8b672cf8618afd5a875b"
            ],
            "version": "==3.6.4.0"
        },
        "blessed": {
            "hashes": [
                "sha256:63b8554ae2e0e7f43749b6715c734cc8f3883010a809bf16790102563e6cf25b",
                "sha256:9a0d099695bf621d4680dd6c73f6ad547f6a3442fbdbe80c4b1daa1edbc492fc"
            ],
            "markers": "python_version >= '2.7'",
            "version": "==1.19.1"
        },
        "celery": {
            "hashes": [
                "sha256:138420c020cd58d6707e6257b6beda91fd39af7afde5d36c6334d175302c0e14",
                "sha256:fafbd82934d30f8a004f81e8f7a062e31413a23d444be8ee3326553915958c6d"
            ],
            "index": "pypi",
            "version": "==5.2.7"
        },
        "certifi": {
            "hashes": [
                "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d",
                "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==2022.6.15"
        },
        "cffi": {
            "hashes": [
                "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5",
                "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef",
                "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104",
                "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426",
                "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405",
                "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375",
                "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a",
                "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e",
                "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc",
                "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf",
                "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185",
                "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497",
                "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3",
                "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35",
                "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c",
                "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83",
                "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21",
                "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca",
                "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984",
                "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac",
                "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd",
                "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee",
                "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a",
                "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2",
                "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192",
                "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7",
                "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585",
                "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f",
                "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e",
                "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27",
                "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b",
                "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e",
                "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e",
                "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d",
                "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c",
                "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415",
                "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82",
                "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02",
                "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314",
                "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325",
                "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c",
                "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3",
                "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914",
                "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045",
                "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d",
                "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9",
                "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5",
                "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2",
                "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c",
                "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3",
                "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2",
                "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8",
                "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d",
                "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d",
                "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9",
                "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162",
                "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76",
                "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4",
                "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e",
                "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9",
                "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6",
                "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b",
                "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01",
                "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"
            ],
            "version": "==1.15.1"
        },
        "cfgv": {
            "hashes": [
                "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426",
                "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"
            ],
            "markers": "python_full_version >= '3.6.1'",
            "version": "==3.3.1"
        },
        "charset-normalizer": {
            "hashes": [
                "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5",
                "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==2.1.0"
        },
        "click": {
            "hashes": [
                "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
                "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==8.1.3"
        },
        "click-didyoumean": {
            "hashes": [
                "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667",
                "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"
            ],
            "markers": "python_full_version >= '3.6.2' and python_full_version < '4.0.0'",
            "version": "==0.3.0"
        },
        "click-plugins": {
            "hashes": [
                "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b",
                "sha256:5d262006d3222f5057fd81e1623d4443e41dcda5dc815c06b442aa3c02889fc8"
            ],
            "version": "==1.1.1"
        },
        "click-repl": {
            "hashes": [
                "sha256:94b3fbbc9406a236f176e0506524b2937e4b23b6f4c0c0b2a0a83f8a64e9194b",
                "sha256:cd12f68d745bf6151210790540b4cb064c7b13e571bc64b6957d98d120dacfd8"
            ],
            "version": "==0.2.0"
        },
        "cryptography": {
            "hashes": [
                "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59",
                "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596",
                "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3",
                "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5",
                "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab",
                "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884",
                "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82",
                "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b",
                "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441",
                "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa",
                "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d",
                "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b",
                "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a",
                "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6",
                "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157",
                "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280",
                "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282",
                "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67",
                "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8",
                "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046",
                "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327",
                "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==37.0.4"
        },
        "distlib": {
            "hashes": [
                "sha256:a7f75737c70be3b25e2bee06288cec4e4c221de18455b2dd037fe2a795cab2fe",
                "sha256:b710088c59f06338ca514800ad795a132da19fda270e3ce4affc74abf955a26c"
            ],
            "version": "==0.3.5"
        },
        "dj-database-url": {
            "hashes": [
                "sha256:ccf3e8718f75ddd147a1e212fca88eecdaa721759ee48e38b485481c77bca3dc",
                "sha256:cd354a3b7a9136d78d64c17b2aec369e2ae5616fbca6bfbe435ef15bb372ce39"
            ],
            "index": "pypi",
            "version": "==1.0.0"
        },
        "django": {
            "hashes": [
                "sha256:502ae42b6ab1b612c933fb50d5ff850facf858a4c212f76946ecd8ea5b3bf2d9",
                "sha256:f7431a5de7277966f3785557c3928433347d998c1e6459324501378a291e5aab"
            ],
            "index": "pypi",
            "version": "==4.0.5"
        },
        "django-appconf": {
            "hashes": [
                "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d",
                "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==1.0.5"
        },
        "django-celery-beat": {
            "hashes": [
                "sha256:ba24f86ed956ba0ee2b43237a49303ebf5aa7e0fb0ccb7e0542b7e31a463dcba",
                "sha256:ff694e1d30f84de3d2b78822c90005c6c7c1462768e845ae162af71a6a46f1eb"
            ],
            "index": "pypi",
            "version": "==2.3.0"
        },
        "django-cors-headers": {
            "hashes": [
                "sha256:37e42883b5f1f2295df6b4bba96eb2417a14a03270cb24b2a07f021cd4487cf4",
                "sha256:f9dc6b4e3f611c3199700b3e5f3398c28757dcd559c2f82932687f3d0443cfdf"
            ],
            "index": "pypi",
            "version": "==3.13.0"
        },
        "django-cryptography": {
            "hashes": [
                "sha256:93702fcf0d75865d55362f20ecd95274c4eef60ccdce46cbdade0420acee07cb"
            ],
            "index": "pypi",
            "version": "==1.1"
        },
        "django-environ": {
            "hashes": [
                "sha256:bff5381533056328c9ac02f71790bd5bf1cea81b1beeb648f28b81c9e83e0a21",
                "sha256:f21a5ef8cc603da1870bbf9a09b7e5577ab5f6da451b843dbcc721a7bca6b3d9"
            ],
            "index": "pypi",
            "version": "==0.9.0"
        },
        "django-extensions": {
            "hashes": [
                "sha256:4c234a7236e9e41c17d9036f6dae7a3a9b212527105b8a0d24b2459b267825f0",
                "sha256:7dc7cd1da50d83b76447a58f5d7e5c8e6cd83f21e9b7e5f97e6b644f4d4e21a6"
            ],
            "index": "pypi",
            "version": "==3.2.0"
        },
        "django-grappelli": {
            "hashes": [
                "sha256:7d60304200f4ea202862fda3b0efaa91c70280841ca22b1b22f49f1017ed2afc",
                "sha256:a98883407f995896f4c4d108ef3ef6cfc00e581211738c5a13c536950a95bcc8"
            ],
            "index": "pypi",
            "version": "==3.0.3"
        },
        "django-heroku": {
            "hashes": [
                "sha256:2bc690aab89eedbe01311752320a9a12e7548e3b0ed102681acc5736a41a4762",
                "sha256:6af4bc3ae4a9b55eaad6dbe5164918982d2762661aebc9f83d9fa49f6009514e"
            ],
            "index": "pypi",
            "version": "==0.3.1"
        },
        "django-loginas": {
            "hashes": [
                "sha256:2d609ddcc68ff840b3e59baa58ac58b870cd1aef491aaab8cd796d022d961c40",
                "sha256:8d81f31d993b3c3c9f9018b385bfbdf74ead320e0a4b18493141d0e2dbc8ad6b"
            ],
            "index": "pypi",
            "version": "==0.3.10"
        },
        "django-model-utils": {
            "hashes": [
                "sha256:a768a25c80514e0ad4e4a6f9c02c44498985f36c5dfdea47b5b1e8cf994beba6",
                "sha256:e7a95e102f9c9653427eadab980d5d59e1dea972913b9c9e01ac37f86bba0ddf"
            ],
            "index": "pypi",
            "version": "==4.2.0"
        },
        "django-money": {
            "hashes": [
                "sha256:1f2a760d5da28de8e1beb05bfc66db0c29b4329c7e382cf69f2577315da3ee91",
                "sha256:4213604537d0e61abb5a7bbb39f0c90bd347a6605c67e96868f5fa9fbbe40278"
            ],
            "index": "pypi",
            "version": "==3.0.0"
        },
        "django-picklefield": {
            "hashes": [
                "sha256:c786cbeda78d6def2b43bff4840d19787809c8909f7ad683961703060398d356",
                "sha256:d77c504df7311e8ec14e8b779f10ca6fec74de6c7f8e2c136e1ef60cf955125d"
            ],
            "markers": "python_version >= '3'",
            "version": "==3.1"
        },
        "django-q": {
            "hashes": [
                "sha256:1b74ce3a8931990b136903e3a7bc9b07243282a2b5355117246f05ed5d076e68",
                "sha256:5c6b4d530aa3aabf9c6aa57376da1ca2abf89a1562b77038b7a04e52a4a0a91b"
            ],
            "index": "pypi",
            "version": "==1.3.9"
        },
        "django-timezone-field": {
            "hashes": [
                "sha256:15746ed367a5a32eda76cfa2886eeec1de8cda79f519b7c5e12f87ed7cdbd663",
                "sha256:199f211082eeac7e83563929b8ce41399c1c0f00dfc2f36bc00bea381027eaaa"
            ],
            "markers": "python_version >= '3.7' and python_full_version < '4.0.0'",
            "version": "==5.0"
        },
        "django-vite": {
            "hashes": [
                "sha256:5a2e11a9f08a2a0a69c9a7c637a3254ad0c8dd01e07cb40de09a3a180ed99791",
                "sha256:f11f4747183b884ee4e6c2ea12f0aa96a846574ee7344c05ac96b4fa89309535"
            ],
            "index": "pypi",
            "version": "==2.0.2"
        },
        "djangorestframework": {
            "hashes": [
                "sha256:0c33407ce23acc68eca2a6e46424b008c9c02eceb8cf18581921d0092bc1f2ee",
                "sha256:24c4bf58ed7e85d1fe4ba250ab2da926d263cd57d64b03e8dcef0ac683f8b1aa"
            ],
            "index": "pypi",
            "version": "==3.13.1"
        },
        "djangorestframework-api-key": {
            "hashes": [
                "sha256:41b7c988f356d62410fb42b9982a3017be035b84d68f187e127c0b52e6557b19",
                "sha256:c9884e52f2802994a02781eaba0a63a628a150ed55d58603d5c9c57a6657de43"
            ],
            "index": "pypi",
            "version": "==2.2.0"
        },
        "filelock": {
            "hashes": [
                "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc",
                "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==3.8.0"
        },
        "gunicorn": {
            "hashes": [
                "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e",
                "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"
            ],
            "index": "pypi",
            "version": "==20.1.0"
        },
        "identify": {
            "hashes": [
                "sha256:25851c8c1370effb22aaa3c987b30449e9ff0cece408f810ae6ce408fdd20893",
                "sha256:887e7b91a1be152b0d46bbf072130235a8117392b9f1828446079a816a05ef44"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==2.5.3"
        },
        "idna": {
            "hashes": [
                "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff",
                "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"
            ],
            "markers": "python_version >= '3.5'",
            "version": "==3.3"
        },
        "jsonfield": {
            "hashes": [
                "sha256:7e4e84597de21eeaeeaaa7cc5da08c61c48a9b64d0c446b2d71255d01812887a",
                "sha256:df857811587f252b97bafba42e02805e70a398a7a47870bc6358a0308dd689ed"
            ],
            "index": "pypi",
            "version": "==3.1.0"
        },
        "kombu": {
            "hashes": [
                "sha256:37cee3ee725f94ea8bb173eaab7c1760203ea53bbebae226328600f9d2799610",
                "sha256:8b213b24293d3417bcf0d2f5537b7f756079e3ea232a8386dcc89a59fd2361a4"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==5.2.4"
        },
        "nodeenv": {
            "hashes": [
                "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e",
                "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"
            ],
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
            "version": "==1.7.0"
        },
        "platformdirs": {
            "hashes": [
                "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788",
                "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==2.5.2"
        },
        "pre-commit": {
            "hashes": [
                "sha256:51a5ba7c480ae8072ecdb6933df22d2f812dc897d5fe848778116129a681aac7",
                "sha256:a978dac7bc9ec0bcee55c18a277d553b0f419d259dadb4b9418ff2d00eb43959"
            ],
            "index": "pypi",
            "version": "==2.20.0"
        },
        "prompt-toolkit": {
            "hashes": [
                "sha256:859b283c50bde45f5f97829f77a4674d1c1fcd88539364f1b28a37805cfd89c0",
                "sha256:d8916d3f62a7b67ab353a952ce4ced6a1d2587dfe9ef8ebc30dd7c386751f289"
            ],
            "markers": "python_full_version >= '3.6.2'",
            "version": "==3.0.30"
        },
        "psycopg2": {
            "hashes": [
                "sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c",
                "sha256:0762c27d018edbcb2d34d51596e4346c983bd27c330218c56c4dc25ef7e819bf",
                "sha256:083707a696e5e1c330af2508d8fab36f9700b26621ccbcb538abe22e15485362",
                "sha256:34b33e0162cfcaad151f249c2649fd1030010c16f4bbc40a604c1cb77173dcf7",
                "sha256:4295093a6ae3434d33ec6baab4ca5512a5082cc43c0505293087b8a46d108461",
                "sha256:8cf3878353cc04b053822896bc4922b194792df9df2f1ad8da01fb3043602126",
                "sha256:8e841d1bf3434da985cc5ef13e6f75c8981ced601fd70cc6bf33351b91562981",
                "sha256:9572e08b50aed176ef6d66f15a21d823bb6f6d23152d35e8451d7d2d18fdac56",
                "sha256:a81e3866f99382dfe8c15a151f1ca5fde5815fde879348fe5a9884a7c092a305",
                "sha256:cb10d44e6694d763fa1078a26f7f6137d69f555a78ec85dc2ef716c37447e4b2",
                "sha256:d3ca6421b942f60c008f81a3541e8faf6865a28d5a9b48544b0ee4f40cac7fca"
            ],
            "index": "pypi",
            "version": "==2.9.3"
        },
        "py-moneyed": {
            "hashes": [
                "sha256:1fafe552cfa3cba579d026924c27b070d71b4140e50ef4535e4083b3f4f2473f",
                "sha256:a56e1987deacb2e0eac5904552699a5d3fa251042e528bf2ff74a72359f5e5b3"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==2.0"
        },
        "pycparser": {
            "hashes": [
                "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9",
                "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"
            ],
            "version": "==2.21"
        },
        "python-crontab": {
            "hashes": [
                "sha256:1e35ed7a3cdc3100545b43e196d34754e6551e7f95e4caebbe0e1c0ca41c2f1b"
            ],
            "version": "==2.6.0"
        },
        "python-dateutil": {
            "hashes": [
                "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86",
                "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"
            ],
            "index": "pypi",
            "version": "==2.8.2"
        },
        "python-dotenv": {
            "hashes": [
                "sha256:b7e3b04a59693c42c36f9ab1cc2acc46fa5df8c78e178fc33a8d4cd05c8d498f",
                "sha256:d92a187be61fe482e4fd675b6d52200e7be63a12b724abbf931a40ce4fa92938"
            ],
            "index": "pypi",
            "version": "==0.20.0"
        },
        "pytz": {
            "hashes": [
                "sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197",
                "sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5"
            ],
            "version": "==2022.2.1"
        },
        "pyyaml": {
            "hashes": [
                "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293",
                "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b",
                "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57",
                "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b",
                "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4",
                "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07",
                "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba",
                "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9",
                "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287",
                "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513",
                "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0",
                "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0",
                "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92",
                "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f",
                "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2",
                "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc",
                "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c",
                "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86",
                "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4",
                "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c",
                "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34",
                "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b",
                "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c",
                "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb",
                "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737",
                "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3",
                "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d",
                "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53",
                "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78",
                "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803",
                "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a",
                "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174",
                "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==6.0"
        },
        "redis": {
            "hashes": [
                "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
                "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"
            ],
            "index": "pypi",
            "version": "==3.5.3"
        },
        "requests": {
            "hashes": [
                "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983",
                "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"
            ],
            "markers": "python_version >= '3'",
            "version": "==2.28.1"
        },
        "sentry-sdk": {
            "hashes": [
                "sha256:2d7ec7bc88ebbdf2c4b6b2650b3257893d386325a96c9b723adcd31033469b63",
                "sha256:b4b41f90951ed83e7b4c176eef021b19ecba39da5b73aca106c97a9b7e279a90"
            ],
            "index": "pypi",
            "version": "==1.9.5"
        },
        "setuptools": {
            "hashes": [
                "sha256:10602cd0a6f5feab6656e9587f9075292ab777c5200f3bf00293ecd23d9f2788",
                "sha256:d2e010624c781b26ad6629a8de9832327cf853dea93894487979e55f9ad06857"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==65.1.0"
        },
        "six": {
            "hashes": [
                "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926",
                "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"
            ],
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
            "version": "==1.16.0"
        },
        "sqlparse": {
            "hashes": [
                "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae",
                "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"
            ],
            "markers": "python_version >= '3.5'",
            "version": "==0.4.2"
        },
        "stripe": {
            "hashes": [
                "sha256:d085b3e9450a5550912f476c6dbb2bafa129e485e24d6f4047c5094ff08d5de2",
                "sha256:de2d532764d07aca0b974ac4ce8d1feee4007cf8c3c2cf6070c16b4cdbb4eb6d"
            ],
            "index": "pypi",
            "version": "==4.0.2"
        },
        "toml": {
            "hashes": [
                "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
                "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
            ],
            "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
            "version": "==0.10.2"
        },
        "typing-extensions": {
            "hashes": [
                "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
                "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==4.3.0"
        },
        "tzdata": {
            "hashes": [
                "sha256:21f4f0d7241572efa7f7a4fdabb052e61b55dc48274e6842697ccdf5253e5451",
                "sha256:c3119520447d68ef3eb8187a55a4f44fa455f30eb1b4238fa5691ba094f2b05b"
            ],
            "markers": "python_version >= '2'",
            "version": "==2022.2"
        },
        "urllib3": {
            "hashes": [
                "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc",
                "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==1.26.11"
        },
        "vine": {
            "hashes": [
                "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30",
                "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==5.0.0"
        },
        "virtualenv": {
            "hashes": [
                "sha256:4193b7bc8a6cd23e4eb251ac64f29b4398ab2c233531e66e40b19a6b7b0d30c1",
                "sha256:d86ea0bb50e06252d79e6c241507cb904fcd66090c3271381372d6221a3970f9"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==20.16.3"
        },
        "wcwidth": {
            "hashes": [
                "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
                "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
            ],
            "version": "==0.2.5"
        },
        "whitenoise": {
            "hashes": [
                "sha256:8e9c600a5c18bd17655ef668ad55b5edf6c24ce9bdca5bf607649ca4b1e8e2c2",
                "sha256:8fa943c6d4cd9e27673b70c21a07b0aa120873901e099cd46cab40f7cc96d567"
            ],
            "index": "pypi",
            "version": "==6.2.0"
        }
    },
    "develop": {
        "astroid": {
            "hashes": [
                "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b",
                "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"
            ],
            "markers": "python_full_version >= '3.6.2'",
            "version": "==2.11.7"
        },
        "black": {
            "hashes": [
                "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90",
                "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c",
                "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78",
                "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4",
                "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee",
                "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e",
                "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e",
                "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6",
                "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9",
                "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c",
                "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256",
                "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f",
                "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2",
                "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c",
                "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b",
                "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807",
                "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf",
                "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def",
                "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad",
                "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d",
                "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849",
                "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69",
                "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"
            ],
            "index": "pypi",
            "version": "==22.6.0"
        },
        "click": {
            "hashes": [
                "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e",
                "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==8.1.3"
        },
        "dill": {
            "hashes": [
                "sha256:33501d03270bbe410c72639b350e941882a8b0fd55357580fbc873fba0c59302",
                "sha256:d75e41f3eff1eee599d738e76ba8f4ad98ea229db8b085318aa2b3333a208c86"
            ],
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'",
            "version": "==0.3.5.1"
        },
        "isort": {
            "hashes": [
                "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7",
                "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"
            ],
            "index": "pypi",
            "version": "==5.10.1"
        },
        "lazy-object-proxy": {
            "hashes": [
                "sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7",
                "sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a",
                "sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c",
                "sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc",
                "sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f",
                "sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09",
                "sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442",
                "sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e",
                "sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029",
                "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61",
                "sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb",
                "sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0",
                "sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35",
                "sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42",
                "sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1",
                "sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad",
                "sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443",
                "sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd",
                "sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9",
                "sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148",
                "sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38",
                "sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55",
                "sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36",
                "sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a",
                "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b",
                "sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44",
                "sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6",
                "sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69",
                "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4",
                "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84",
                "sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de",
                "sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28",
                "sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c",
                "sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1",
                "sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8",
                "sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b",
                "sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==1.7.1"
        },
        "mccabe": {
            "hashes": [
                "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325",
                "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"
            ],
            "markers": "python_version >= '3.6'",
            "version": "==0.7.0"
        },
        "mypy-extensions": {
            "hashes": [
                "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
                "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
            ],
            "version": "==0.4.3"
        },
        "pathspec": {
            "hashes": [
                "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a",
                "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"
            ],
            "version": "==0.9.0"
        },
        "platformdirs": {
            "hashes": [
                "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788",
                "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==2.5.2"
        },
        "pylint": {
            "hashes": [
                "sha256:487ce2192eee48211269a0e976421f334cf94de1806ca9d0a99449adcdf0285e",
                "sha256:fabe30000de7d07636d2e82c9a518ad5ad7908590fe135ace169b44839c15f90"
            ],
            "index": "pypi",
            "version": "==2.14.5"
        },
        "setuptools": {
            "hashes": [
                "sha256:10602cd0a6f5feab6656e9587f9075292ab777c5200f3bf00293ecd23d9f2788",
                "sha256:d2e010624c781b26ad6629a8de9832327cf853dea93894487979e55f9ad06857"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==65.1.0"
        },
        "tomli": {
            "hashes": [
                "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
                "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
            ],
            "markers": "python_version < '3.11'",
            "version": "==2.0.1"
        },
        "tomlkit": {
            "hashes": [
                "sha256:25d4e2e446c453be6360c67ddfb88838cfc42026322770ba13d1fbd403a93a5c",
                "sha256:3235a9010fae54323e727c3ac06fb720752fe6635b3426e379daec60fbd44a83"
            ],
            "markers": "python_version >= '3.6' and python_version < '4.0'",
            "version": "==0.11.4"
        },
        "typing-extensions": {
            "hashes": [
                "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02",
                "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"
            ],
            "markers": "python_version >= '3.7'",
            "version": "==4.3.0"
        },
        "wrapt": {
            "hashes": [
                "sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3",
                "sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b",
                "sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4",
                "sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2",
                "sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656",
                "sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3",
                "sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff",
                "sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310",
                "sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a",
                "sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57",
                "sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069",
                "sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383",
                "sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe",
                "sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87",
                "sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d",
                "sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b",
                "sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907",
                "sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f",
                "sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0",
                "sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28",
                "sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1",
                "sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853",
                "sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc",
                "sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3",
                "sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3",
                "sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164",
                "sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1",
                "sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c",
                "sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1",
                "sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7",
                "sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1",
                "sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320",
                "sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed",
                "sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1",
                "sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248",
                "sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c",
                "sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456",
                "sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77",
                "sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef",
                "sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1",
                "sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7",
                "sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86",
                "sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4",
                "sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d",
                "sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d",
                "sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8",
                "sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5",
                "sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471",
                "sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00",
                "sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68",
                "sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3",
                "sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d",
                "sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735",
                "sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d",
                "sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569",
                "sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7",
                "sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59",
                "sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5",
                "sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb",
                "sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b",
                "sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f",
                "sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462",
                "sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015",
                "sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af"
            ],
            "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
            "version": "==1.14.1"
        }
    }
}

Pipfile METASEP
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
celery = ">=5.2.7,<5.3"
django = "==4.0.5"
django-cors-headers = ">=3.13.0,<3.14"
django-cryptography = ">=1.1.0,<1.2"
django-extensions = ">=3.2.0,<3.3"
django-grappelli = ">=3.0.3,<3.4"
django-loginas = ">=0.3.10,<0.4"
django-model-utils = ">=4.2.0,<4.3"
django-money = ">=3.0.0,<3.1"
django-q = ">=1.3.9,<1.4"
djangorestframework = ">=3.13.1,<3.14"
djangorestframework-api-key = ">=2.2.0,<2.3"
jsonfield = ">=3.1.0,<3.2"
pre-commit = ">=2.20.0,<2.21"
psycopg2 = ">=2.9.3,<2.10"
python-dateutil = ">=2.8.2,<2.9"
python-dotenv = ">=0.20.0,<0.21"
redis = ">=3.5.3,<4.0"
sentry-sdk = ">=1.9.0,<1.10"
stripe = ">=4.0.2,<4.1"
gunicorn = "*"
whitenoise = "*"
dj-database-url = "*"
django-heroku = "*"
django-celery-beat = "*"
django-vite = "*"
django-environ = "*"

[dev-packages]
pylint = ">=2.14.5,<2.15"
black = "*"
platformdirs = "*"
isort = "*"

[requires]
python_version = "3.9"

Dockerfile.frontend METASEP
FROM node:18.7.0-alpine
WORKDIR /frontend

COPY package.json ./
RUN yarn install 
COPY . ./
EXPOSE 3000
Dockerfile METASEP
# using official Docker Python BaseImage
#!/bin/bash
FROM --platform=linux/amd64 python:3.9-bullseye


WORKDIR /usr/src/app


COPY Pipfile Pipfile.lock ./

# install pipenv in container
RUN pip install -U --no-cache-dir --disable-pip-version-check pipenv

# install the packages/dependencies required for the project
RUN pipenv install --system --deploy --ignore-pipfile
# copy all files from the <src> directory to the <dest> directory
COPY . .

EXPOSE 8000

# Run Server

CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "lotus.wsgi:application"]

CONTRIBUTING.md METASEP
There are many ways to contribute to Lotus.

# Creating a PR

Just create a new pull request if you want to make an update.

For now, please request Mikael (mikael@uselotus.io) to do the QA. Email him if you have any questions as well.

# Issues

Spotted a bug? Has deployment gone wrong? Do you have user feedback? Raise an issue for the fastest response.

... or pick up and fix an issue if you want to do a Pull Request.

# Feature requests

Raise an issue for these and tag it as an Enhancement. We love every idea. Please give us as much context on the why as possible.

AWSCLIV2.pkg METASEP

metering_billing/template_tags/render_vite_bundle.py METASEP
# This template tag is needed for production
# Add it to one of your django apps (/appdir/templatetags/render_vite_bundle.py, for example)

import json
import os

from django import template
from django.conf import settings
from django.utils.safestring import mark_safe

register = template.Library()


@register.simple_tag
def render_vite_bundle():
    """
    Template tag to render a vite bundle.
    Supposed to only be used in production.
    For development, see other files.
    """

    try:
        fd = open(f"{settings.VITE_APP_DIR}/dist/manifest.json", "r")
        manifest = json.load(fd)
    except:
        raise Exception(
            f"Vite manifest file not found or invalid. Maybe your {settings.VITE_APP_DIR}/dist/manifest.json file is empty?"
        )

    imports_files = "".join(
        [
            f'<script type="module" src="/{manifest[file]["file"]}"></script>'
            for file in manifest["index.html"]["imports"]
        ]
    )

    return mark_safe(
        f"""<script type="module" src="/{manifest['index.html']['file']}"></script>
        <link rel="stylesheet" type="text/css" href="/{manifest['index.html']['css'][0]}" />
        {imports_files}"""
    )

metering_billing/template_tags/__init__.py METASEP

metering_billing/migrations/__init__.py METASEP

metering_billing/migrations/0001_initial.py METASEP
# Generated by Django 4.0.5 on 2022-08-15 04:35

import uuid
from decimal import Decimal

import django.contrib.auth.models
import django.contrib.auth.validators
import django.contrib.postgres.fields
import django.db.models.deletion
import django.utils.timezone
import djmoney.models.fields
from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ("auth", "0012_alter_user_first_name_max_length"),
    ]

    operations = [
        migrations.CreateModel(
            name="User",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("password", models.CharField(max_length=128, verbose_name="password")),
                (
                    "last_login",
                    models.DateTimeField(
                        blank=True, null=True, verbose_name="last login"
                    ),
                ),
                (
                    "is_superuser",
                    models.BooleanField(
                        default=False,
                        help_text="Designates that this user has all permissions without explicitly assigning them.",
                        verbose_name="superuser status",
                    ),
                ),
                (
                    "username",
                    models.CharField(
                        error_messages={
                            "unique": "A user with that username already exists."
                        },
                        help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
                        max_length=150,
                        unique=True,
                        validators=[
                            django.contrib.auth.validators.UnicodeUsernameValidator()
                        ],
                        verbose_name="username",
                    ),
                ),
                (
                    "first_name",
                    models.CharField(
                        blank=True, max_length=150, verbose_name="first name"
                    ),
                ),
                (
                    "last_name",
                    models.CharField(
                        blank=True, max_length=150, verbose_name="last name"
                    ),
                ),
                (
                    "email",
                    models.EmailField(
                        blank=True, max_length=254, verbose_name="email address"
                    ),
                ),
                (
                    "is_staff",
                    models.BooleanField(
                        default=False,
                        help_text="Designates whether the user can log into this admin site.",
                        verbose_name="staff status",
                    ),
                ),
                (
                    "is_active",
                    models.BooleanField(
                        default=True,
                        help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
                        verbose_name="active",
                    ),
                ),
                (
                    "date_joined",
                    models.DateTimeField(
                        default=django.utils.timezone.now, verbose_name="date joined"
                    ),
                ),
                ("company_name", models.CharField(default=" ", max_length=200)),
                (
                    "groups",
                    models.ManyToManyField(
                        blank=True,
                        help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
                        related_name="user_set",
                        related_query_name="user",
                        to="auth.group",
                        verbose_name="groups",
                    ),
                ),
                (
                    "user_permissions",
                    models.ManyToManyField(
                        blank=True,
                        help_text="Specific permissions for this user.",
                        related_name="user_set",
                        related_query_name="user",
                        to="auth.permission",
                        verbose_name="user permissions",
                    ),
                ),
            ],
            options={
                "verbose_name": "user",
                "verbose_name_plural": "users",
                "abstract": False,
            },
            managers=[
                ("objects", django.contrib.auth.models.UserManager()),
            ],
        ),
        migrations.CreateModel(
            name="BillableMetric",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("event_name", models.CharField(max_length=200)),
                ("property_name", models.CharField(max_length=200, null=True)),
                (
                    "aggregation_type",
                    models.CharField(
                        choices=[("count", "Count"), ("sum", "Sum"), ("max", "Max")],
                        default="count",
                        max_length=10,
                    ),
                ),
            ],
        ),
        migrations.CreateModel(
            name="BillingPlan",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                (
                    "plan_id",
                    models.CharField(
                        blank=True, default=uuid.uuid4, max_length=36, unique=True
                    ),
                ),
                ("time_created", models.DateTimeField(auto_now=True)),
                ("currency", models.CharField(default="USD", max_length=30)),
                (
                    "interval",
                    models.CharField(
                        choices=[
                            ("week", "Week"),
                            ("month", "Month"),
                            ("year", "Year"),
                        ],
                        default="month",
                        max_length=5,
                    ),
                ),
                (
                    "flat_rate_currency",
                    djmoney.models.fields.CurrencyField(
                        choices=[
                            ("XUA", "ADB Unit of Account"),
                            ("AFN", "Afghan Afghani"),
                            ("AFA", "Afghan Afghani (1927–2002)"),
                            ("ALL", "Albanian Lek"),
                            ("ALK", "Albanian Lek (1946–1965)"),
                            ("DZD", "Algerian Dinar"),
                            ("ADP", "Andorran Peseta"),
                            ("AOA", "Angolan Kwanza"),
                            ("AOK", "Angolan Kwanza (1977–1991)"),
                            ("AON", "Angolan New Kwanza (1990–2000)"),
                            ("AOR", "Angolan Readjusted Kwanza (1995–1999)"),
                            ("ARA", "Argentine Austral"),
                            ("ARS", "Argentine Peso"),
                            ("ARM", "Argentine Peso (1881–1970)"),
                            ("ARP", "Argentine Peso (1983–1985)"),
                            ("ARL", "Argentine Peso Ley (1970–1983)"),
                            ("AMD", "Armenian Dram"),
                            ("AWG", "Aruban Florin"),
                            ("AUD", "Australian Dollar"),
                            ("ATS", "Austrian Schilling"),
                            ("AZN", "Azerbaijani Manat"),
                            ("AZM", "Azerbaijani Manat (1993–2006)"),
                            ("BSD", "Bahamian Dollar"),
                            ("BHD", "Bahraini Dinar"),
                            ("BDT", "Bangladeshi Taka"),
                            ("BBD", "Barbadian Dollar"),
                            ("BYN", "Belarusian Ruble"),
                            ("BYB", "Belarusian Ruble (1994–1999)"),
                            ("BYR", "Belarusian Ruble (2000–2016)"),
                            ("BEF", "Belgian Franc"),
                            ("BEC", "Belgian Franc (convertible)"),
                            ("BEL", "Belgian Franc (financial)"),
                            ("BZD", "Belize Dollar"),
                            ("BMD", "Bermudan Dollar"),
                            ("BTN", "Bhutanese Ngultrum"),
                            ("BOB", "Bolivian Boliviano"),
                            ("BOL", "Bolivian Boliviano (1863–1963)"),
                            ("BOV", "Bolivian Mvdol"),
                            ("BOP", "Bolivian Peso"),
                            ("BAM", "Bosnia-Herzegovina Convertible Mark"),
                            ("BAD", "Bosnia-Herzegovina Dinar (1992–1994)"),
                            ("BAN", "Bosnia-Herzegovina New Dinar (1994–1997)"),
                            ("BWP", "Botswanan Pula"),
                            ("BRC", "Brazilian Cruzado (1986–1989)"),
                            ("BRZ", "Brazilian Cruzeiro (1942–1967)"),
                            ("BRE", "Brazilian Cruzeiro (1990–1993)"),
                            ("BRR", "Brazilian Cruzeiro (1993–1994)"),
                            ("BRN", "Brazilian New Cruzado (1989–1990)"),
                            ("BRB", "Brazilian New Cruzeiro (1967–1986)"),
                            ("BRL", "Brazilian Real"),
                            ("GBP", "British Pound"),
                            ("BND", "Brunei Dollar"),
                            ("BGL", "Bulgarian Hard Lev"),
                            ("BGN", "Bulgarian Lev"),
                            ("BGO", "Bulgarian Lev (1879–1952)"),
                            ("BGM", "Bulgarian Socialist Lev"),
                            ("BUK", "Burmese Kyat"),
                            ("BIF", "Burundian Franc"),
                            ("XPF", "CFP Franc"),
                            ("KHR", "Cambodian Riel"),
                            ("CAD", "Canadian Dollar"),
                            ("CVE", "Cape Verdean Escudo"),
                            ("KYD", "Cayman Islands Dollar"),
                            ("XAF", "Central African CFA Franc"),
                            ("CLE", "Chilean Escudo"),
                            ("CLP", "Chilean Peso"),
                            ("CLF", "Chilean Unit of Account (UF)"),
                            ("CNX", "Chinese People’s Bank Dollar"),
                            ("CNY", "Chinese Yuan"),
                            ("CNH", "Chinese Yuan (offshore)"),
                            ("COP", "Colombian Peso"),
                            ("COU", "Colombian Real Value Unit"),
                            ("KMF", "Comorian Franc"),
                            ("CDF", "Congolese Franc"),
                            ("CRC", "Costa Rican Colón"),
                            ("HRD", "Croatian Dinar"),
                            ("HRK", "Croatian Kuna"),
                            ("CUC", "Cuban Convertible Peso"),
                            ("CUP", "Cuban Peso"),
                            ("CYP", "Cypriot Pound"),
                            ("CZK", "Czech Koruna"),
                            ("CSK", "Czechoslovak Hard Koruna"),
                            ("DKK", "Danish Krone"),
                            ("DJF", "Djiboutian Franc"),
                            ("DOP", "Dominican Peso"),
                            ("NLG", "Dutch Guilder"),
                            ("XCD", "East Caribbean Dollar"),
                            ("DDM", "East German Mark"),
                            ("ECS", "Ecuadorian Sucre"),
                            ("ECV", "Ecuadorian Unit of Constant Value"),
                            ("EGP", "Egyptian Pound"),
                            ("GQE", "Equatorial Guinean Ekwele"),
                            ("ERN", "Eritrean Nakfa"),
                            ("EEK", "Estonian Kroon"),
                            ("ETB", "Ethiopian Birr"),
                            ("EUR", "Euro"),
                            ("XBA", "European Composite Unit"),
                            ("XEU", "European Currency Unit"),
                            ("XBB", "European Monetary Unit"),
                            ("XBC", "European Unit of Account (XBC)"),
                            ("XBD", "European Unit of Account (XBD)"),
                            ("FKP", "Falkland Islands Pound"),
                            ("FJD", "Fijian Dollar"),
                            ("FIM", "Finnish Markka"),
                            ("FRF", "French Franc"),
                            ("XFO", "French Gold Franc"),
                            ("XFU", "French UIC-Franc"),
                            ("GMD", "Gambian Dalasi"),
                            ("GEK", "Georgian Kupon Larit"),
                            ("GEL", "Georgian Lari"),
                            ("DEM", "German Mark"),
                            ("GHS", "Ghanaian Cedi"),
                            ("GHC", "Ghanaian Cedi (1979–2007)"),
                            ("GIP", "Gibraltar Pound"),
                            ("XAU", "Gold"),
                            ("GRD", "Greek Drachma"),
                            ("GTQ", "Guatemalan Quetzal"),
                            ("GWP", "Guinea-Bissau Peso"),
                            ("GNF", "Guinean Franc"),
                            ("GNS", "Guinean Syli"),
                            ("GYD", "Guyanaese Dollar"),
                            ("HTG", "Haitian Gourde"),
                            ("HNL", "Honduran Lempira"),
                            ("HKD", "Hong Kong Dollar"),
                            ("HUF", "Hungarian Forint"),
                            ("IMP", "IMP"),
                            ("ISK", "Icelandic Króna"),
                            ("ISJ", "Icelandic Króna (1918–1981)"),
                            ("INR", "Indian Rupee"),
                            ("IDR", "Indonesian Rupiah"),
                            ("IRR", "Iranian Rial"),
                            ("IQD", "Iraqi Dinar"),
                            ("IEP", "Irish Pound"),
                            ("ILS", "Israeli New Shekel"),
                            ("ILP", "Israeli Pound"),
                            ("ILR", "Israeli Shekel (1980–1985)"),
                            ("ITL", "Italian Lira"),
                            ("JMD", "Jamaican Dollar"),
                            ("JPY", "Japanese Yen"),
                            ("JOD", "Jordanian Dinar"),
                            ("KZT", "Kazakhstani Tenge"),
                            ("KES", "Kenyan Shilling"),
                            ("KWD", "Kuwaiti Dinar"),
                            ("KGS", "Kyrgystani Som"),
                            ("LAK", "Laotian Kip"),
                            ("LVL", "Latvian Lats"),
                            ("LVR", "Latvian Ruble"),
                            ("LBP", "Lebanese Pound"),
                            ("LSL", "Lesotho Loti"),
                            ("LRD", "Liberian Dollar"),
                            ("LYD", "Libyan Dinar"),
                            ("LTL", "Lithuanian Litas"),
                            ("LTT", "Lithuanian Talonas"),
                            ("LUL", "Luxembourg Financial Franc"),
                            ("LUC", "Luxembourgian Convertible Franc"),
                            ("LUF", "Luxembourgian Franc"),
                            ("MOP", "Macanese Pataca"),
                            ("MKD", "Macedonian Denar"),
                            ("MKN", "Macedonian Denar (1992–1993)"),
                            ("MGA", "Malagasy Ariary"),
                            ("MGF", "Malagasy Franc"),
                            ("MWK", "Malawian Kwacha"),
                            ("MYR", "Malaysian Ringgit"),
                            ("MVR", "Maldivian Rufiyaa"),
                            ("MVP", "Maldivian Rupee (1947–1981)"),
                            ("MLF", "Malian Franc"),
                            ("MTL", "Maltese Lira"),
                            ("MTP", "Maltese Pound"),
                            ("MRU", "Mauritanian Ouguiya"),
                            ("MRO", "Mauritanian Ouguiya (1973–2017)"),
                            ("MUR", "Mauritian Rupee"),
                            ("MXV", "Mexican Investment Unit"),
                            ("MXN", "Mexican Peso"),
                            ("MXP", "Mexican Silver Peso (1861–1992)"),
                            ("MDC", "Moldovan Cupon"),
                            ("MDL", "Moldovan Leu"),
                            ("MCF", "Monegasque Franc"),
                            ("MNT", "Mongolian Tugrik"),
                            ("MAD", "Moroccan Dirham"),
                            ("MAF", "Moroccan Franc"),
                            ("MZE", "Mozambican Escudo"),
                            ("MZN", "Mozambican Metical"),
                            ("MZM", "Mozambican Metical (1980–2006)"),
                            ("MMK", "Myanmar Kyat"),
                            ("NAD", "Namibian Dollar"),
                            ("NPR", "Nepalese Rupee"),
                            ("ANG", "Netherlands Antillean Guilder"),
                            ("TWD", "New Taiwan Dollar"),
                            ("NZD", "New Zealand Dollar"),
                            ("NIO", "Nicaraguan Córdoba"),
                            ("NIC", "Nicaraguan Córdoba (1988–1991)"),
                            ("NGN", "Nigerian Naira"),
                            ("KPW", "North Korean Won"),
                            ("NOK", "Norwegian Krone"),
                            ("OMR", "Omani Rial"),
                            ("PKR", "Pakistani Rupee"),
                            ("XPD", "Palladium"),
                            ("PAB", "Panamanian Balboa"),
                            ("PGK", "Papua New Guinean Kina"),
                            ("PYG", "Paraguayan Guarani"),
                            ("PEI", "Peruvian Inti"),
                            ("PEN", "Peruvian Sol"),
                            ("PES", "Peruvian Sol (1863–1965)"),
                            ("PHP", "Philippine Peso"),
                            ("XPT", "Platinum"),
                            ("PLN", "Polish Zloty"),
                            ("PLZ", "Polish Zloty (1950–1995)"),
                            ("PTE", "Portuguese Escudo"),
                            ("GWE", "Portuguese Guinea Escudo"),
                            ("QAR", "Qatari Rial"),
                            ("XRE", "RINET Funds"),
                            ("RHD", "Rhodesian Dollar"),
                            ("RON", "Romanian Leu"),
                            ("ROL", "Romanian Leu (1952–2006)"),
                            ("RUB", "Russian Ruble"),
                            ("RUR", "Russian Ruble (1991–1998)"),
                            ("RWF", "Rwandan Franc"),
                            ("SVC", "Salvadoran Colón"),
                            ("WST", "Samoan Tala"),
                            ("SAR", "Saudi Riyal"),
                            ("RSD", "Serbian Dinar"),
                            ("CSD", "Serbian Dinar (2002–2006)"),
                            ("SCR", "Seychellois Rupee"),
                            ("SLL", "Sierra Leonean Leone"),
                            ("XAG", "Silver"),
                            ("SGD", "Singapore Dollar"),
                            ("SKK", "Slovak Koruna"),
                            ("SIT", "Slovenian Tolar"),
                            ("SBD", "Solomon Islands Dollar"),
                            ("SOS", "Somali Shilling"),
                            ("ZAR", "South African Rand"),
                            ("ZAL", "South African Rand (financial)"),
                            ("KRH", "South Korean Hwan (1953–1962)"),
                            ("KRW", "South Korean Won"),
                            ("KRO", "South Korean Won (1945–1953)"),
                            ("SSP", "South Sudanese Pound"),
                            ("SUR", "Soviet Rouble"),
                            ("ESP", "Spanish Peseta"),
                            ("ESA", "Spanish Peseta (A account)"),
                            ("ESB", "Spanish Peseta (convertible account)"),
                            ("XDR", "Special Drawing Rights"),
                            ("LKR", "Sri Lankan Rupee"),
                            ("SHP", "St. Helena Pound"),
                            ("XSU", "Sucre"),
                            ("SDD", "Sudanese Dinar (1992–2007)"),
                            ("SDG", "Sudanese Pound"),
                            ("SDP", "Sudanese Pound (1957–1998)"),
                            ("SRD", "Surinamese Dollar"),
                            ("SRG", "Surinamese Guilder"),
                            ("SZL", "Swazi Lilangeni"),
                            ("SEK", "Swedish Krona"),
                            ("CHF", "Swiss Franc"),
                            ("SYP", "Syrian Pound"),
                            ("STN", "São Tomé & Príncipe Dobra"),
                            ("STD", "São Tomé & Príncipe Dobra (1977–2017)"),
                            ("TVD", "TVD"),
                            ("TJR", "Tajikistani Ruble"),
                            ("TJS", "Tajikistani Somoni"),
                            ("TZS", "Tanzanian Shilling"),
                            ("XTS", "Testing Currency Code"),
                            ("THB", "Thai Baht"),
                            (
                                "XXX",
                                "The codes assigned for transactions where no currency is involved",
                            ),
                            ("TPE", "Timorese Escudo"),
                            ("TOP", "Tongan Paʻanga"),
                            ("TTD", "Trinidad & Tobago Dollar"),
                            ("TND", "Tunisian Dinar"),
                            ("TRY", "Turkish Lira"),
                            ("TRL", "Turkish Lira (1922–2005)"),
                            ("TMT", "Turkmenistani Manat"),
                            ("TMM", "Turkmenistani Manat (1993–2009)"),
                            ("USD", "US Dollar"),
                            ("USN", "US Dollar (Next day)"),
                            ("USS", "US Dollar (Same day)"),
                            ("UGX", "Ugandan Shilling"),
                            ("UGS", "Ugandan Shilling (1966–1987)"),
                            ("UAH", "Ukrainian Hryvnia"),
                            ("UAK", "Ukrainian Karbovanets"),
                            ("AED", "United Arab Emirates Dirham"),
                            ("UYW", "Uruguayan Nominal Wage Index Unit"),
                            ("UYU", "Uruguayan Peso"),
                            ("UYP", "Uruguayan Peso (1975–1993)"),
                            ("UYI", "Uruguayan Peso (Indexed Units)"),
                            ("UZS", "Uzbekistani Som"),
                            ("VUV", "Vanuatu Vatu"),
                            ("VES", "Venezuelan Bolívar"),
                            ("VEB", "Venezuelan Bolívar (1871–2008)"),
                            ("VEF", "Venezuelan Bolívar (2008–2018)"),
                            ("VND", "Vietnamese Dong"),
                            ("VNN", "Vietnamese Dong (1978–1985)"),
                            ("CHE", "WIR Euro"),
                            ("CHW", "WIR Franc"),
                            ("XOF", "West African CFA Franc"),
                            ("YDD", "Yemeni Dinar"),
                            ("YER", "Yemeni Rial"),
                            ("YUN", "Yugoslavian Convertible Dinar (1990–1992)"),
                            ("YUD", "Yugoslavian Hard Dinar (1966–1990)"),
                            ("YUM", "Yugoslavian New Dinar (1994–2002)"),
                            ("YUR", "Yugoslavian Reformed Dinar (1992–1993)"),
                            ("ZWN", "ZWN"),
                            ("ZRN", "Zairean New Zaire (1993–1998)"),
                            ("ZRZ", "Zairean Zaire (1971–1993)"),
                            ("ZMW", "Zambian Kwacha"),
                            ("ZMK", "Zambian Kwacha (1968–2012)"),
                            ("ZWD", "Zimbabwean Dollar (1980–2008)"),
                            ("ZWR", "Zimbabwean Dollar (2008)"),
                            ("ZWL", "Zimbabwean Dollar (2009)"),
                        ],
                        default="USD",
                        editable=False,
                        max_length=3,
                    ),
                ),
                (
                    "flat_rate",
                    djmoney.models.fields.MoneyField(
                        decimal_places=2,
                        default=Decimal("0.0"),
                        default_currency="USD",
                        max_digits=8,
                    ),
                ),
                ("pay_in_advance", models.BooleanField(default=False)),
                ("name", models.CharField(default=" ", max_length=200)),
                (
                    "description",
                    models.CharField(blank=True, default=" ", max_length=256),
                ),
            ],
        ),
        migrations.CreateModel(
            name="Customer",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("name", models.CharField(max_length=100)),
                ("customer_id", models.CharField(max_length=40, unique=True)),
                ("billing_id", models.CharField(default=uuid.uuid4, max_length=40)),
                (
                    "balance_currency",
                    djmoney.models.fields.CurrencyField(
                        choices=[
                            ("XUA", "ADB Unit of Account"),
                            ("AFN", "Afghan Afghani"),
                            ("AFA", "Afghan Afghani (1927–2002)"),
                            ("ALL", "Albanian Lek"),
                            ("ALK", "Albanian Lek (1946–1965)"),
                            ("DZD", "Algerian Dinar"),
                            ("ADP", "Andorran Peseta"),
                            ("AOA", "Angolan Kwanza"),
                            ("AOK", "Angolan Kwanza (1977–1991)"),
                            ("AON", "Angolan New Kwanza (1990–2000)"),
                            ("AOR", "Angolan Readjusted Kwanza (1995–1999)"),
                            ("ARA", "Argentine Austral"),
                            ("ARS", "Argentine Peso"),
                            ("ARM", "Argentine Peso (1881–1970)"),
                            ("ARP", "Argentine Peso (1983–1985)"),
                            ("ARL", "Argentine Peso Ley (1970–1983)"),
                            ("AMD", "Armenian Dram"),
                            ("AWG", "Aruban Florin"),
                            ("AUD", "Australian Dollar"),
                            ("ATS", "Austrian Schilling"),
                            ("AZN", "Azerbaijani Manat"),
                            ("AZM", "Azerbaijani Manat (1993–2006)"),
                            ("BSD", "Bahamian Dollar"),
                            ("BHD", "Bahraini Dinar"),
                            ("BDT", "Bangladeshi Taka"),
                            ("BBD", "Barbadian Dollar"),
                            ("BYN", "Belarusian Ruble"),
                            ("BYB", "Belarusian Ruble (1994–1999)"),
                            ("BYR", "Belarusian Ruble (2000–2016)"),
                            ("BEF", "Belgian Franc"),
                            ("BEC", "Belgian Franc (convertible)"),
                            ("BEL", "Belgian Franc (financial)"),
                            ("BZD", "Belize Dollar"),
                            ("BMD", "Bermudan Dollar"),
                            ("BTN", "Bhutanese Ngultrum"),
                            ("BOB", "Bolivian Boliviano"),
                            ("BOL", "Bolivian Boliviano (1863–1963)"),
                            ("BOV", "Bolivian Mvdol"),
                            ("BOP", "Bolivian Peso"),
                            ("BAM", "Bosnia-Herzegovina Convertible Mark"),
                            ("BAD", "Bosnia-Herzegovina Dinar (1992–1994)"),
                            ("BAN", "Bosnia-Herzegovina New Dinar (1994–1997)"),
                            ("BWP", "Botswanan Pula"),
                            ("BRC", "Brazilian Cruzado (1986–1989)"),
                            ("BRZ", "Brazilian Cruzeiro (1942–1967)"),
                            ("BRE", "Brazilian Cruzeiro (1990–1993)"),
                            ("BRR", "Brazilian Cruzeiro (1993–1994)"),
                            ("BRN", "Brazilian New Cruzado (1989–1990)"),
                            ("BRB", "Brazilian New Cruzeiro (1967–1986)"),
                            ("BRL", "Brazilian Real"),
                            ("GBP", "British Pound"),
                            ("BND", "Brunei Dollar"),
                            ("BGL", "Bulgarian Hard Lev"),
                            ("BGN", "Bulgarian Lev"),
                            ("BGO", "Bulgarian Lev (1879–1952)"),
                            ("BGM", "Bulgarian Socialist Lev"),
                            ("BUK", "Burmese Kyat"),
                            ("BIF", "Burundian Franc"),
                            ("XPF", "CFP Franc"),
                            ("KHR", "Cambodian Riel"),
                            ("CAD", "Canadian Dollar"),
                            ("CVE", "Cape Verdean Escudo"),
                            ("KYD", "Cayman Islands Dollar"),
                            ("XAF", "Central African CFA Franc"),
                            ("CLE", "Chilean Escudo"),
                            ("CLP", "Chilean Peso"),
                            ("CLF", "Chilean Unit of Account (UF)"),
                            ("CNX", "Chinese People’s Bank Dollar"),
                            ("CNY", "Chinese Yuan"),
                            ("CNH", "Chinese Yuan (offshore)"),
                            ("COP", "Colombian Peso"),
                            ("COU", "Colombian Real Value Unit"),
                            ("KMF", "Comorian Franc"),
                            ("CDF", "Congolese Franc"),
                            ("CRC", "Costa Rican Colón"),
                            ("HRD", "Croatian Dinar"),
                            ("HRK", "Croatian Kuna"),
                            ("CUC", "Cuban Convertible Peso"),
                            ("CUP", "Cuban Peso"),
                            ("CYP", "Cypriot Pound"),
                            ("CZK", "Czech Koruna"),
                            ("CSK", "Czechoslovak Hard Koruna"),
                            ("DKK", "Danish Krone"),
                            ("DJF", "Djiboutian Franc"),
                            ("DOP", "Dominican Peso"),
                            ("NLG", "Dutch Guilder"),
                            ("XCD", "East Caribbean Dollar"),
                            ("DDM", "East German Mark"),
                            ("ECS", "Ecuadorian Sucre"),
                            ("ECV", "Ecuadorian Unit of Constant Value"),
                            ("EGP", "Egyptian Pound"),
                            ("GQE", "Equatorial Guinean Ekwele"),
                            ("ERN", "Eritrean Nakfa"),
                            ("EEK", "Estonian Kroon"),
                            ("ETB", "Ethiopian Birr"),
                            ("EUR", "Euro"),
                            ("XBA", "European Composite Unit"),
                            ("XEU", "European Currency Unit"),
                            ("XBB", "European Monetary Unit"),
                            ("XBC", "European Unit of Account (XBC)"),
                            ("XBD", "European Unit of Account (XBD)"),
                            ("FKP", "Falkland Islands Pound"),
                            ("FJD", "Fijian Dollar"),
                            ("FIM", "Finnish Markka"),
                            ("FRF", "French Franc"),
                            ("XFO", "French Gold Franc"),
                            ("XFU", "French UIC-Franc"),
                            ("GMD", "Gambian Dalasi"),
                            ("GEK", "Georgian Kupon Larit"),
                            ("GEL", "Georgian Lari"),
                            ("DEM", "German Mark"),
                            ("GHS", "Ghanaian Cedi"),
                            ("GHC", "Ghanaian Cedi (1979–2007)"),
                            ("GIP", "Gibraltar Pound"),
                            ("XAU", "Gold"),
                            ("GRD", "Greek Drachma"),
                            ("GTQ", "Guatemalan Quetzal"),
                            ("GWP", "Guinea-Bissau Peso"),
                            ("GNF", "Guinean Franc"),
                            ("GNS", "Guinean Syli"),
                            ("GYD", "Guyanaese Dollar"),
                            ("HTG", "Haitian Gourde"),
                            ("HNL", "Honduran Lempira"),
                            ("HKD", "Hong Kong Dollar"),
                            ("HUF", "Hungarian Forint"),
                            ("IMP", "IMP"),
                            ("ISK", "Icelandic Króna"),
                            ("ISJ", "Icelandic Króna (1918–1981)"),
                            ("INR", "Indian Rupee"),
                            ("IDR", "Indonesian Rupiah"),
                            ("IRR", "Iranian Rial"),
                            ("IQD", "Iraqi Dinar"),
                            ("IEP", "Irish Pound"),
                            ("ILS", "Israeli New Shekel"),
                            ("ILP", "Israeli Pound"),
                            ("ILR", "Israeli Shekel (1980–1985)"),
                            ("ITL", "Italian Lira"),
                            ("JMD", "Jamaican Dollar"),
                            ("JPY", "Japanese Yen"),
                            ("JOD", "Jordanian Dinar"),
                            ("KZT", "Kazakhstani Tenge"),
                            ("KES", "Kenyan Shilling"),
                            ("KWD", "Kuwaiti Dinar"),
                            ("KGS", "Kyrgystani Som"),
                            ("LAK", "Laotian Kip"),
                            ("LVL", "Latvian Lats"),
                            ("LVR", "Latvian Ruble"),
                            ("LBP", "Lebanese Pound"),
                            ("LSL", "Lesotho Loti"),
                            ("LRD", "Liberian Dollar"),
                            ("LYD", "Libyan Dinar"),
                            ("LTL", "Lithuanian Litas"),
                            ("LTT", "Lithuanian Talonas"),
                            ("LUL", "Luxembourg Financial Franc"),
                            ("LUC", "Luxembourgian Convertible Franc"),
                            ("LUF", "Luxembourgian Franc"),
                            ("MOP", "Macanese Pataca"),
                            ("MKD", "Macedonian Denar"),
                            ("MKN", "Macedonian Denar (1992–1993)"),
                            ("MGA", "Malagasy Ariary"),
                            ("MGF", "Malagasy Franc"),
                            ("MWK", "Malawian Kwacha"),
                            ("MYR", "Malaysian Ringgit"),
                            ("MVR", "Maldivian Rufiyaa"),
                            ("MVP", "Maldivian Rupee (1947–1981)"),
                            ("MLF", "Malian Franc"),
                            ("MTL", "Maltese Lira"),
                            ("MTP", "Maltese Pound"),
                            ("MRU", "Mauritanian Ouguiya"),
                            ("MRO", "Mauritanian Ouguiya (1973–2017)"),
                            ("MUR", "Mauritian Rupee"),
                            ("MXV", "Mexican Investment Unit"),
                            ("MXN", "Mexican Peso"),
                            ("MXP", "Mexican Silver Peso (1861–1992)"),
                            ("MDC", "Moldovan Cupon"),
                            ("MDL", "Moldovan Leu"),
                            ("MCF", "Monegasque Franc"),
                            ("MNT", "Mongolian Tugrik"),
                            ("MAD", "Moroccan Dirham"),
                            ("MAF", "Moroccan Franc"),
                            ("MZE", "Mozambican Escudo"),
                            ("MZN", "Mozambican Metical"),
                            ("MZM", "Mozambican Metical (1980–2006)"),
                            ("MMK", "Myanmar Kyat"),
                            ("NAD", "Namibian Dollar"),
                            ("NPR", "Nepalese Rupee"),
                            ("ANG", "Netherlands Antillean Guilder"),
                            ("TWD", "New Taiwan Dollar"),
                            ("NZD", "New Zealand Dollar"),
                            ("NIO", "Nicaraguan Córdoba"),
                            ("NIC", "Nicaraguan Córdoba (1988–1991)"),
                            ("NGN", "Nigerian Naira"),
                            ("KPW", "North Korean Won"),
                            ("NOK", "Norwegian Krone"),
                            ("OMR", "Omani Rial"),
                            ("PKR", "Pakistani Rupee"),
                            ("XPD", "Palladium"),
                            ("PAB", "Panamanian Balboa"),
                            ("PGK", "Papua New Guinean Kina"),
                            ("PYG", "Paraguayan Guarani"),
                            ("PEI", "Peruvian Inti"),
                            ("PEN", "Peruvian Sol"),
                            ("PES", "Peruvian Sol (1863–1965)"),
                            ("PHP", "Philippine Peso"),
                            ("XPT", "Platinum"),
                            ("PLN", "Polish Zloty"),
                            ("PLZ", "Polish Zloty (1950–1995)"),
                            ("PTE", "Portuguese Escudo"),
                            ("GWE", "Portuguese Guinea Escudo"),
                            ("QAR", "Qatari Rial"),
                            ("XRE", "RINET Funds"),
                            ("RHD", "Rhodesian Dollar"),
                            ("RON", "Romanian Leu"),
                            ("ROL", "Romanian Leu (1952–2006)"),
                            ("RUB", "Russian Ruble"),
                            ("RUR", "Russian Ruble (1991–1998)"),
                            ("RWF", "Rwandan Franc"),
                            ("SVC", "Salvadoran Colón"),
                            ("WST", "Samoan Tala"),
                            ("SAR", "Saudi Riyal"),
                            ("RSD", "Serbian Dinar"),
                            ("CSD", "Serbian Dinar (2002–2006)"),
                            ("SCR", "Seychellois Rupee"),
                            ("SLL", "Sierra Leonean Leone"),
                            ("XAG", "Silver"),
                            ("SGD", "Singapore Dollar"),
                            ("SKK", "Slovak Koruna"),
                            ("SIT", "Slovenian Tolar"),
                            ("SBD", "Solomon Islands Dollar"),
                            ("SOS", "Somali Shilling"),
                            ("ZAR", "South African Rand"),
                            ("ZAL", "South African Rand (financial)"),
                            ("KRH", "South Korean Hwan (1953–1962)"),
                            ("KRW", "South Korean Won"),
                            ("KRO", "South Korean Won (1945–1953)"),
                            ("SSP", "South Sudanese Pound"),
                            ("SUR", "Soviet Rouble"),
                            ("ESP", "Spanish Peseta"),
                            ("ESA", "Spanish Peseta (A account)"),
                            ("ESB", "Spanish Peseta (convertible account)"),
                            ("XDR", "Special Drawing Rights"),
                            ("LKR", "Sri Lankan Rupee"),
                            ("SHP", "St. Helena Pound"),
                            ("XSU", "Sucre"),
                            ("SDD", "Sudanese Dinar (1992–2007)"),
                            ("SDG", "Sudanese Pound"),
                            ("SDP", "Sudanese Pound (1957–1998)"),
                            ("SRD", "Surinamese Dollar"),
                            ("SRG", "Surinamese Guilder"),
                            ("SZL", "Swazi Lilangeni"),
                            ("SEK", "Swedish Krona"),
                            ("CHF", "Swiss Franc"),
                            ("SYP", "Syrian Pound"),
                            ("STN", "São Tomé & Príncipe Dobra"),
                            ("STD", "São Tomé & Príncipe Dobra (1977–2017)"),
                            ("TVD", "TVD"),
                            ("TJR", "Tajikistani Ruble"),
                            ("TJS", "Tajikistani Somoni"),
                            ("TZS", "Tanzanian Shilling"),
                            ("XTS", "Testing Currency Code"),
                            ("THB", "Thai Baht"),
                            (
                                "XXX",
                                "The codes assigned for transactions where no currency is involved",
                            ),
                            ("TPE", "Timorese Escudo"),
                            ("TOP", "Tongan Paʻanga"),
                            ("TTD", "Trinidad & Tobago Dollar"),
                            ("TND", "Tunisian Dinar"),
                            ("TRY", "Turkish Lira"),
                            ("TRL", "Turkish Lira (1922–2005)"),
                            ("TMT", "Turkmenistani Manat"),
                            ("TMM", "Turkmenistani Manat (1993–2009)"),
                            ("USD", "US Dollar"),
                            ("USN", "US Dollar (Next day)"),
                            ("USS", "US Dollar (Same day)"),
                            ("UGX", "Ugandan Shilling"),
                            ("UGS", "Ugandan Shilling (1966–1987)"),
                            ("UAH", "Ukrainian Hryvnia"),
                            ("UAK", "Ukrainian Karbovanets"),
                            ("AED", "United Arab Emirates Dirham"),
                            ("UYW", "Uruguayan Nominal Wage Index Unit"),
                            ("UYU", "Uruguayan Peso"),
                            ("UYP", "Uruguayan Peso (1975–1993)"),
                            ("UYI", "Uruguayan Peso (Indexed Units)"),
                            ("UZS", "Uzbekistani Som"),
                            ("VUV", "Vanuatu Vatu"),
                            ("VES", "Venezuelan Bolívar"),
                            ("VEB", "Venezuelan Bolívar (1871–2008)"),
                            ("VEF", "Venezuelan Bolívar (2008–2018)"),
                            ("VND", "Vietnamese Dong"),
                            ("VNN", "Vietnamese Dong (1978–1985)"),
                            ("CHE", "WIR Euro"),
                            ("CHW", "WIR Franc"),
                            ("XOF", "West African CFA Franc"),
                            ("YDD", "Yemeni Dinar"),
                            ("YER", "Yemeni Rial"),
                            ("YUN", "Yugoslavian Convertible Dinar (1990–1992)"),
                            ("YUD", "Yugoslavian Hard Dinar (1966–1990)"),
                            ("YUM", "Yugoslavian New Dinar (1994–2002)"),
                            ("YUR", "Yugoslavian Reformed Dinar (1992–1993)"),
                            ("ZWN", "ZWN"),
                            ("ZRN", "Zairean New Zaire (1993–1998)"),
                            ("ZRZ", "Zairean Zaire (1971–1993)"),
                            ("ZMW", "Zambian Kwacha"),
                            ("ZMK", "Zambian Kwacha (1968–2012)"),
                            ("ZWD", "Zimbabwean Dollar (1980–2008)"),
                            ("ZWR", "Zimbabwean Dollar (2008)"),
                            ("ZWL", "Zimbabwean Dollar (2009)"),
                        ],
                        default="USD",
                        editable=False,
                        max_length=3,
                    ),
                ),
                (
                    "balance",
                    djmoney.models.fields.MoneyField(
                        decimal_places=2,
                        default=Decimal("0"),
                        default_currency="USD",
                        max_digits=10,
                    ),
                ),
                ("currency", models.CharField(default="USD", max_length=3)),
                (
                    "payment_provider_id",
                    models.CharField(blank=True, max_length=50, null=True),
                ),
            ],
        ),
        migrations.CreateModel(
            name="Organization",
            fields=[
                ("company_name", models.CharField(default=" ", max_length=100)),
                (
                    "stripe_api_key",
                    models.CharField(blank=True, default="", max_length=110),
                ),
                (
                    "payment_plan",
                    models.CharField(
                        choices=[
                            ("self_hosted_free", "Self-Hosted Free"),
                            ("cloud", "Cloud"),
                            ("self_hosted_enterprise", "Self-Hosted Enterprise"),
                        ],
                        default="self_hosted_free",
                        max_length=40,
                    ),
                ),
                (
                    "id",
                    models.CharField(
                        default=uuid.uuid4,
                        max_length=40,
                        primary_key=True,
                        serialize=False,
                        unique=True,
                    ),
                ),
                ("created_on", models.DateField(auto_now_add=True)),
                (
                    "users",
                    models.ManyToManyField(blank=True, to=settings.AUTH_USER_MODEL),
                ),
            ],
        ),
        migrations.CreateModel(
            name="Subscription",
            fields=[
                (
                    "id",
                    models.CharField(
                        default=uuid.uuid4,
                        max_length=36,
                        primary_key=True,
                        serialize=False,
                    ),
                ),
                ("start_date", models.DateField()),
                ("end_date", models.DateField()),
                (
                    "status",
                    models.CharField(
                        choices=[("active", "Active"), ("ended", "Ended")],
                        default="active",
                        max_length=6,
                    ),
                ),
                (
                    "billing_plan",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.billingplan",
                    ),
                ),
                (
                    "customer",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.customer",
                    ),
                ),
                (
                    "organization",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.organization",
                    ),
                ),
            ],
            options={
                "unique_together": {("start_date", "end_date")},
            },
        ),
        migrations.CreateModel(
            name="PlanComponent",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("free_metric_quantity", models.IntegerField(default=0)),
                (
                    "cost_per_metric_currency",
                    djmoney.models.fields.CurrencyField(
                        choices=[
                            ("XUA", "ADB Unit of Account"),
                            ("AFN", "Afghan Afghani"),
                            ("AFA", "Afghan Afghani (1927–2002)"),
                            ("ALL", "Albanian Lek"),
                            ("ALK", "Albanian Lek (1946–1965)"),
                            ("DZD", "Algerian Dinar"),
                            ("ADP", "Andorran Peseta"),
                            ("AOA", "Angolan Kwanza"),
                            ("AOK", "Angolan Kwanza (1977–1991)"),
                            ("AON", "Angolan New Kwanza (1990–2000)"),
                            ("AOR", "Angolan Readjusted Kwanza (1995–1999)"),
                            ("ARA", "Argentine Austral"),
                            ("ARS", "Argentine Peso"),
                            ("ARM", "Argentine Peso (1881–1970)"),
                            ("ARP", "Argentine Peso (1983–1985)"),
                            ("ARL", "Argentine Peso Ley (1970–1983)"),
                            ("AMD", "Armenian Dram"),
                            ("AWG", "Aruban Florin"),
                            ("AUD", "Australian Dollar"),
                            ("ATS", "Austrian Schilling"),
                            ("AZN", "Azerbaijani Manat"),
                            ("AZM", "Azerbaijani Manat (1993–2006)"),
                            ("BSD", "Bahamian Dollar"),
                            ("BHD", "Bahraini Dinar"),
                            ("BDT", "Bangladeshi Taka"),
                            ("BBD", "Barbadian Dollar"),
                            ("BYN", "Belarusian Ruble"),
                            ("BYB", "Belarusian Ruble (1994–1999)"),
                            ("BYR", "Belarusian Ruble (2000–2016)"),
                            ("BEF", "Belgian Franc"),
                            ("BEC", "Belgian Franc (convertible)"),
                            ("BEL", "Belgian Franc (financial)"),
                            ("BZD", "Belize Dollar"),
                            ("BMD", "Bermudan Dollar"),
                            ("BTN", "Bhutanese Ngultrum"),
                            ("BOB", "Bolivian Boliviano"),
                            ("BOL", "Bolivian Boliviano (1863–1963)"),
                            ("BOV", "Bolivian Mvdol"),
                            ("BOP", "Bolivian Peso"),
                            ("BAM", "Bosnia-Herzegovina Convertible Mark"),
                            ("BAD", "Bosnia-Herzegovina Dinar (1992–1994)"),
                            ("BAN", "Bosnia-Herzegovina New Dinar (1994–1997)"),
                            ("BWP", "Botswanan Pula"),
                            ("BRC", "Brazilian Cruzado (1986–1989)"),
                            ("BRZ", "Brazilian Cruzeiro (1942–1967)"),
                            ("BRE", "Brazilian Cruzeiro (1990–1993)"),
                            ("BRR", "Brazilian Cruzeiro (1993–1994)"),
                            ("BRN", "Brazilian New Cruzado (1989–1990)"),
                            ("BRB", "Brazilian New Cruzeiro (1967–1986)"),
                            ("BRL", "Brazilian Real"),
                            ("GBP", "British Pound"),
                            ("BND", "Brunei Dollar"),
                            ("BGL", "Bulgarian Hard Lev"),
                            ("BGN", "Bulgarian Lev"),
                            ("BGO", "Bulgarian Lev (1879–1952)"),
                            ("BGM", "Bulgarian Socialist Lev"),
                            ("BUK", "Burmese Kyat"),
                            ("BIF", "Burundian Franc"),
                            ("XPF", "CFP Franc"),
                            ("KHR", "Cambodian Riel"),
                            ("CAD", "Canadian Dollar"),
                            ("CVE", "Cape Verdean Escudo"),
                            ("KYD", "Cayman Islands Dollar"),
                            ("XAF", "Central African CFA Franc"),
                            ("CLE", "Chilean Escudo"),
                            ("CLP", "Chilean Peso"),
                            ("CLF", "Chilean Unit of Account (UF)"),
                            ("CNX", "Chinese People’s Bank Dollar"),
                            ("CNY", "Chinese Yuan"),
                            ("CNH", "Chinese Yuan (offshore)"),
                            ("COP", "Colombian Peso"),
                            ("COU", "Colombian Real Value Unit"),
                            ("KMF", "Comorian Franc"),
                            ("CDF", "Congolese Franc"),
                            ("CRC", "Costa Rican Colón"),
                            ("HRD", "Croatian Dinar"),
                            ("HRK", "Croatian Kuna"),
                            ("CUC", "Cuban Convertible Peso"),
                            ("CUP", "Cuban Peso"),
                            ("CYP", "Cypriot Pound"),
                            ("CZK", "Czech Koruna"),
                            ("CSK", "Czechoslovak Hard Koruna"),
                            ("DKK", "Danish Krone"),
                            ("DJF", "Djiboutian Franc"),
                            ("DOP", "Dominican Peso"),
                            ("NLG", "Dutch Guilder"),
                            ("XCD", "East Caribbean Dollar"),
                            ("DDM", "East German Mark"),
                            ("ECS", "Ecuadorian Sucre"),
                            ("ECV", "Ecuadorian Unit of Constant Value"),
                            ("EGP", "Egyptian Pound"),
                            ("GQE", "Equatorial Guinean Ekwele"),
                            ("ERN", "Eritrean Nakfa"),
                            ("EEK", "Estonian Kroon"),
                            ("ETB", "Ethiopian Birr"),
                            ("EUR", "Euro"),
                            ("XBA", "European Composite Unit"),
                            ("XEU", "European Currency Unit"),
                            ("XBB", "European Monetary Unit"),
                            ("XBC", "European Unit of Account (XBC)"),
                            ("XBD", "European Unit of Account (XBD)"),
                            ("FKP", "Falkland Islands Pound"),
                            ("FJD", "Fijian Dollar"),
                            ("FIM", "Finnish Markka"),
                            ("FRF", "French Franc"),
                            ("XFO", "French Gold Franc"),
                            ("XFU", "French UIC-Franc"),
                            ("GMD", "Gambian Dalasi"),
                            ("GEK", "Georgian Kupon Larit"),
                            ("GEL", "Georgian Lari"),
                            ("DEM", "German Mark"),
                            ("GHS", "Ghanaian Cedi"),
                            ("GHC", "Ghanaian Cedi (1979–2007)"),
                            ("GIP", "Gibraltar Pound"),
                            ("XAU", "Gold"),
                            ("GRD", "Greek Drachma"),
                            ("GTQ", "Guatemalan Quetzal"),
                            ("GWP", "Guinea-Bissau Peso"),
                            ("GNF", "Guinean Franc"),
                            ("GNS", "Guinean Syli"),
                            ("GYD", "Guyanaese Dollar"),
                            ("HTG", "Haitian Gourde"),
                            ("HNL", "Honduran Lempira"),
                            ("HKD", "Hong Kong Dollar"),
                            ("HUF", "Hungarian Forint"),
                            ("IMP", "IMP"),
                            ("ISK", "Icelandic Króna"),
                            ("ISJ", "Icelandic Króna (1918–1981)"),
                            ("INR", "Indian Rupee"),
                            ("IDR", "Indonesian Rupiah"),
                            ("IRR", "Iranian Rial"),
                            ("IQD", "Iraqi Dinar"),
                            ("IEP", "Irish Pound"),
                            ("ILS", "Israeli New Shekel"),
                            ("ILP", "Israeli Pound"),
                            ("ILR", "Israeli Shekel (1980–1985)"),
                            ("ITL", "Italian Lira"),
                            ("JMD", "Jamaican Dollar"),
                            ("JPY", "Japanese Yen"),
                            ("JOD", "Jordanian Dinar"),
                            ("KZT", "Kazakhstani Tenge"),
                            ("KES", "Kenyan Shilling"),
                            ("KWD", "Kuwaiti Dinar"),
                            ("KGS", "Kyrgystani Som"),
                            ("LAK", "Laotian Kip"),
                            ("LVL", "Latvian Lats"),
                            ("LVR", "Latvian Ruble"),
                            ("LBP", "Lebanese Pound"),
                            ("LSL", "Lesotho Loti"),
                            ("LRD", "Liberian Dollar"),
                            ("LYD", "Libyan Dinar"),
                            ("LTL", "Lithuanian Litas"),
                            ("LTT", "Lithuanian Talonas"),
                            ("LUL", "Luxembourg Financial Franc"),
                            ("LUC", "Luxembourgian Convertible Franc"),
                            ("LUF", "Luxembourgian Franc"),
                            ("MOP", "Macanese Pataca"),
                            ("MKD", "Macedonian Denar"),
                            ("MKN", "Macedonian Denar (1992–1993)"),
                            ("MGA", "Malagasy Ariary"),
                            ("MGF", "Malagasy Franc"),
                            ("MWK", "Malawian Kwacha"),
                            ("MYR", "Malaysian Ringgit"),
                            ("MVR", "Maldivian Rufiyaa"),
                            ("MVP", "Maldivian Rupee (1947–1981)"),
                            ("MLF", "Malian Franc"),
                            ("MTL", "Maltese Lira"),
                            ("MTP", "Maltese Pound"),
                            ("MRU", "Mauritanian Ouguiya"),
                            ("MRO", "Mauritanian Ouguiya (1973–2017)"),
                            ("MUR", "Mauritian Rupee"),
                            ("MXV", "Mexican Investment Unit"),
                            ("MXN", "Mexican Peso"),
                            ("MXP", "Mexican Silver Peso (1861–1992)"),
                            ("MDC", "Moldovan Cupon"),
                            ("MDL", "Moldovan Leu"),
                            ("MCF", "Monegasque Franc"),
                            ("MNT", "Mongolian Tugrik"),
                            ("MAD", "Moroccan Dirham"),
                            ("MAF", "Moroccan Franc"),
                            ("MZE", "Mozambican Escudo"),
                            ("MZN", "Mozambican Metical"),
                            ("MZM", "Mozambican Metical (1980–2006)"),
                            ("MMK", "Myanmar Kyat"),
                            ("NAD", "Namibian Dollar"),
                            ("NPR", "Nepalese Rupee"),
                            ("ANG", "Netherlands Antillean Guilder"),
                            ("TWD", "New Taiwan Dollar"),
                            ("NZD", "New Zealand Dollar"),
                            ("NIO", "Nicaraguan Córdoba"),
                            ("NIC", "Nicaraguan Córdoba (1988–1991)"),
                            ("NGN", "Nigerian Naira"),
                            ("KPW", "North Korean Won"),
                            ("NOK", "Norwegian Krone"),
                            ("OMR", "Omani Rial"),
                            ("PKR", "Pakistani Rupee"),
                            ("XPD", "Palladium"),
                            ("PAB", "Panamanian Balboa"),
                            ("PGK", "Papua New Guinean Kina"),
                            ("PYG", "Paraguayan Guarani"),
                            ("PEI", "Peruvian Inti"),
                            ("PEN", "Peruvian Sol"),
                            ("PES", "Peruvian Sol (1863–1965)"),
                            ("PHP", "Philippine Peso"),
                            ("XPT", "Platinum"),
                            ("PLN", "Polish Zloty"),
                            ("PLZ", "Polish Zloty (1950–1995)"),
                            ("PTE", "Portuguese Escudo"),
                            ("GWE", "Portuguese Guinea Escudo"),
                            ("QAR", "Qatari Rial"),
                            ("XRE", "RINET Funds"),
                            ("RHD", "Rhodesian Dollar"),
                            ("RON", "Romanian Leu"),
                            ("ROL", "Romanian Leu (1952–2006)"),
                            ("RUB", "Russian Ruble"),
                            ("RUR", "Russian Ruble (1991–1998)"),
                            ("RWF", "Rwandan Franc"),
                            ("SVC", "Salvadoran Colón"),
                            ("WST", "Samoan Tala"),
                            ("SAR", "Saudi Riyal"),
                            ("RSD", "Serbian Dinar"),
                            ("CSD", "Serbian Dinar (2002–2006)"),
                            ("SCR", "Seychellois Rupee"),
                            ("SLL", "Sierra Leonean Leone"),
                            ("XAG", "Silver"),
                            ("SGD", "Singapore Dollar"),
                            ("SKK", "Slovak Koruna"),
                            ("SIT", "Slovenian Tolar"),
                            ("SBD", "Solomon Islands Dollar"),
                            ("SOS", "Somali Shilling"),
                            ("ZAR", "South African Rand"),
                            ("ZAL", "South African Rand (financial)"),
                            ("KRH", "South Korean Hwan (1953–1962)"),
                            ("KRW", "South Korean Won"),
                            ("KRO", "South Korean Won (1945–1953)"),
                            ("SSP", "South Sudanese Pound"),
                            ("SUR", "Soviet Rouble"),
                            ("ESP", "Spanish Peseta"),
                            ("ESA", "Spanish Peseta (A account)"),
                            ("ESB", "Spanish Peseta (convertible account)"),
                            ("XDR", "Special Drawing Rights"),
                            ("LKR", "Sri Lankan Rupee"),
                            ("SHP", "St. Helena Pound"),
                            ("XSU", "Sucre"),
                            ("SDD", "Sudanese Dinar (1992–2007)"),
                            ("SDG", "Sudanese Pound"),
                            ("SDP", "Sudanese Pound (1957–1998)"),
                            ("SRD", "Surinamese Dollar"),
                            ("SRG", "Surinamese Guilder"),
                            ("SZL", "Swazi Lilangeni"),
                            ("SEK", "Swedish Krona"),
                            ("CHF", "Swiss Franc"),
                            ("SYP", "Syrian Pound"),
                            ("STN", "São Tomé & Príncipe Dobra"),
                            ("STD", "São Tomé & Príncipe Dobra (1977–2017)"),
                            ("TVD", "TVD"),
                            ("TJR", "Tajikistani Ruble"),
                            ("TJS", "Tajikistani Somoni"),
                            ("TZS", "Tanzanian Shilling"),
                            ("XTS", "Testing Currency Code"),
                            ("THB", "Thai Baht"),
                            (
                                "XXX",
                                "The codes assigned for transactions where no currency is involved",
                            ),
                            ("TPE", "Timorese Escudo"),
                            ("TOP", "Tongan Paʻanga"),
                            ("TTD", "Trinidad & Tobago Dollar"),
                            ("TND", "Tunisian Dinar"),
                            ("TRY", "Turkish Lira"),
                            ("TRL", "Turkish Lira (1922–2005)"),
                            ("TMT", "Turkmenistani Manat"),
                            ("TMM", "Turkmenistani Manat (1993–2009)"),
                            ("USD", "US Dollar"),
                            ("USN", "US Dollar (Next day)"),
                            ("USS", "US Dollar (Same day)"),
                            ("UGX", "Ugandan Shilling"),
                            ("UGS", "Ugandan Shilling (1966–1987)"),
                            ("UAH", "Ukrainian Hryvnia"),
                            ("UAK", "Ukrainian Karbovanets"),
                            ("AED", "United Arab Emirates Dirham"),
                            ("UYW", "Uruguayan Nominal Wage Index Unit"),
                            ("UYU", "Uruguayan Peso"),
                            ("UYP", "Uruguayan Peso (1975–1993)"),
                            ("UYI", "Uruguayan Peso (Indexed Units)"),
                            ("UZS", "Uzbekistani Som"),
                            ("VUV", "Vanuatu Vatu"),
                            ("VES", "Venezuelan Bolívar"),
                            ("VEB", "Venezuelan Bolívar (1871–2008)"),
                            ("VEF", "Venezuelan Bolívar (2008–2018)"),
                            ("VND", "Vietnamese Dong"),
                            ("VNN", "Vietnamese Dong (1978–1985)"),
                            ("CHE", "WIR Euro"),
                            ("CHW", "WIR Franc"),
                            ("XOF", "West African CFA Franc"),
                            ("YDD", "Yemeni Dinar"),
                            ("YER", "Yemeni Rial"),
                            ("YUN", "Yugoslavian Convertible Dinar (1990–1992)"),
                            ("YUD", "Yugoslavian Hard Dinar (1966–1990)"),
                            ("YUM", "Yugoslavian New Dinar (1994–2002)"),
                            ("YUR", "Yugoslavian Reformed Dinar (1992–1993)"),
                            ("ZWN", "ZWN"),
                            ("ZRN", "Zairean New Zaire (1993–1998)"),
                            ("ZRZ", "Zairean Zaire (1971–1993)"),
                            ("ZMW", "Zambian Kwacha"),
                            ("ZMK", "Zambian Kwacha (1968–2012)"),
                            ("ZWD", "Zimbabwean Dollar (1980–2008)"),
                            ("ZWR", "Zimbabwean Dollar (2008)"),
                            ("ZWL", "Zimbabwean Dollar (2009)"),
                        ],
                        default="USD",
                        editable=False,
                        max_length=3,
                    ),
                ),
                (
                    "cost_per_metric",
                    djmoney.models.fields.MoneyField(
                        decimal_places=10, default_currency="USD", max_digits=14
                    ),
                ),
                ("metric_amount_per_cost", models.IntegerField(default=1)),
                (
                    "billable_metric",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.billablemetric",
                    ),
                ),
                (
                    "billing_plan",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.billingplan",
                    ),
                ),
            ],
        ),
        migrations.CreateModel(
            name="Invoice",
            fields=[
                (
                    "id",
                    models.CharField(
                        default=uuid.uuid4,
                        max_length=36,
                        primary_key=True,
                        serialize=False,
                    ),
                ),
                (
                    "cost_due_currency",
                    djmoney.models.fields.CurrencyField(
                        choices=[
                            ("XUA", "ADB Unit of Account"),
                            ("AFN", "Afghan Afghani"),
                            ("AFA", "Afghan Afghani (1927–2002)"),
                            ("ALL", "Albanian Lek"),
                            ("ALK", "Albanian Lek (1946–1965)"),
                            ("DZD", "Algerian Dinar"),
                            ("ADP", "Andorran Peseta"),
                            ("AOA", "Angolan Kwanza"),
                            ("AOK", "Angolan Kwanza (1977–1991)"),
                            ("AON", "Angolan New Kwanza (1990–2000)"),
                            ("AOR", "Angolan Readjusted Kwanza (1995–1999)"),
                            ("ARA", "Argentine Austral"),
                            ("ARS", "Argentine Peso"),
                            ("ARM", "Argentine Peso (1881–1970)"),
                            ("ARP", "Argentine Peso (1983–1985)"),
                            ("ARL", "Argentine Peso Ley (1970–1983)"),
                            ("AMD", "Armenian Dram"),
                            ("AWG", "Aruban Florin"),
                            ("AUD", "Australian Dollar"),
                            ("ATS", "Austrian Schilling"),
                            ("AZN", "Azerbaijani Manat"),
                            ("AZM", "Azerbaijani Manat (1993–2006)"),
                            ("BSD", "Bahamian Dollar"),
                            ("BHD", "Bahraini Dinar"),
                            ("BDT", "Bangladeshi Taka"),
                            ("BBD", "Barbadian Dollar"),
                            ("BYN", "Belarusian Ruble"),
                            ("BYB", "Belarusian Ruble (1994–1999)"),
                            ("BYR", "Belarusian Ruble (2000–2016)"),
                            ("BEF", "Belgian Franc"),
                            ("BEC", "Belgian Franc (convertible)"),
                            ("BEL", "Belgian Franc (financial)"),
                            ("BZD", "Belize Dollar"),
                            ("BMD", "Bermudan Dollar"),
                            ("BTN", "Bhutanese Ngultrum"),
                            ("BOB", "Bolivian Boliviano"),
                            ("BOL", "Bolivian Boliviano (1863–1963)"),
                            ("BOV", "Bolivian Mvdol"),
                            ("BOP", "Bolivian Peso"),
                            ("BAM", "Bosnia-Herzegovina Convertible Mark"),
                            ("BAD", "Bosnia-Herzegovina Dinar (1992–1994)"),
                            ("BAN", "Bosnia-Herzegovina New Dinar (1994–1997)"),
                            ("BWP", "Botswanan Pula"),
                            ("BRC", "Brazilian Cruzado (1986–1989)"),
                            ("BRZ", "Brazilian Cruzeiro (1942–1967)"),
                            ("BRE", "Brazilian Cruzeiro (1990–1993)"),
                            ("BRR", "Brazilian Cruzeiro (1993–1994)"),
                            ("BRN", "Brazilian New Cruzado (1989–1990)"),
                            ("BRB", "Brazilian New Cruzeiro (1967–1986)"),
                            ("BRL", "Brazilian Real"),
                            ("GBP", "British Pound"),
                            ("BND", "Brunei Dollar"),
                            ("BGL", "Bulgarian Hard Lev"),
                            ("BGN", "Bulgarian Lev"),
                            ("BGO", "Bulgarian Lev (1879–1952)"),
                            ("BGM", "Bulgarian Socialist Lev"),
                            ("BUK", "Burmese Kyat"),
                            ("BIF", "Burundian Franc"),
                            ("XPF", "CFP Franc"),
                            ("KHR", "Cambodian Riel"),
                            ("CAD", "Canadian Dollar"),
                            ("CVE", "Cape Verdean Escudo"),
                            ("KYD", "Cayman Islands Dollar"),
                            ("XAF", "Central African CFA Franc"),
                            ("CLE", "Chilean Escudo"),
                            ("CLP", "Chilean Peso"),
                            ("CLF", "Chilean Unit of Account (UF)"),
                            ("CNX", "Chinese People’s Bank Dollar"),
                            ("CNY", "Chinese Yuan"),
                            ("CNH", "Chinese Yuan (offshore)"),
                            ("COP", "Colombian Peso"),
                            ("COU", "Colombian Real Value Unit"),
                            ("KMF", "Comorian Franc"),
                            ("CDF", "Congolese Franc"),
                            ("CRC", "Costa Rican Colón"),
                            ("HRD", "Croatian Dinar"),
                            ("HRK", "Croatian Kuna"),
                            ("CUC", "Cuban Convertible Peso"),
                            ("CUP", "Cuban Peso"),
                            ("CYP", "Cypriot Pound"),
                            ("CZK", "Czech Koruna"),
                            ("CSK", "Czechoslovak Hard Koruna"),
                            ("DKK", "Danish Krone"),
                            ("DJF", "Djiboutian Franc"),
                            ("DOP", "Dominican Peso"),
                            ("NLG", "Dutch Guilder"),
                            ("XCD", "East Caribbean Dollar"),
                            ("DDM", "East German Mark"),
                            ("ECS", "Ecuadorian Sucre"),
                            ("ECV", "Ecuadorian Unit of Constant Value"),
                            ("EGP", "Egyptian Pound"),
                            ("GQE", "Equatorial Guinean Ekwele"),
                            ("ERN", "Eritrean Nakfa"),
                            ("EEK", "Estonian Kroon"),
                            ("ETB", "Ethiopian Birr"),
                            ("EUR", "Euro"),
                            ("XBA", "European Composite Unit"),
                            ("XEU", "European Currency Unit"),
                            ("XBB", "European Monetary Unit"),
                            ("XBC", "European Unit of Account (XBC)"),
                            ("XBD", "European Unit of Account (XBD)"),
                            ("FKP", "Falkland Islands Pound"),
                            ("FJD", "Fijian Dollar"),
                            ("FIM", "Finnish Markka"),
                            ("FRF", "French Franc"),
                            ("XFO", "French Gold Franc"),
                            ("XFU", "French UIC-Franc"),
                            ("GMD", "Gambian Dalasi"),
                            ("GEK", "Georgian Kupon Larit"),
                            ("GEL", "Georgian Lari"),
                            ("DEM", "German Mark"),
                            ("GHS", "Ghanaian Cedi"),
                            ("GHC", "Ghanaian Cedi (1979–2007)"),
                            ("GIP", "Gibraltar Pound"),
                            ("XAU", "Gold"),
                            ("GRD", "Greek Drachma"),
                            ("GTQ", "Guatemalan Quetzal"),
                            ("GWP", "Guinea-Bissau Peso"),
                            ("GNF", "Guinean Franc"),
                            ("GNS", "Guinean Syli"),
                            ("GYD", "Guyanaese Dollar"),
                            ("HTG", "Haitian Gourde"),
                            ("HNL", "Honduran Lempira"),
                            ("HKD", "Hong Kong Dollar"),
                            ("HUF", "Hungarian Forint"),
                            ("IMP", "IMP"),
                            ("ISK", "Icelandic Króna"),
                            ("ISJ", "Icelandic Króna (1918–1981)"),
                            ("INR", "Indian Rupee"),
                            ("IDR", "Indonesian Rupiah"),
                            ("IRR", "Iranian Rial"),
                            ("IQD", "Iraqi Dinar"),
                            ("IEP", "Irish Pound"),
                            ("ILS", "Israeli New Shekel"),
                            ("ILP", "Israeli Pound"),
                            ("ILR", "Israeli Shekel (1980–1985)"),
                            ("ITL", "Italian Lira"),
                            ("JMD", "Jamaican Dollar"),
                            ("JPY", "Japanese Yen"),
                            ("JOD", "Jordanian Dinar"),
                            ("KZT", "Kazakhstani Tenge"),
                            ("KES", "Kenyan Shilling"),
                            ("KWD", "Kuwaiti Dinar"),
                            ("KGS", "Kyrgystani Som"),
                            ("LAK", "Laotian Kip"),
                            ("LVL", "Latvian Lats"),
                            ("LVR", "Latvian Ruble"),
                            ("LBP", "Lebanese Pound"),
                            ("LSL", "Lesotho Loti"),
                            ("LRD", "Liberian Dollar"),
                            ("LYD", "Libyan Dinar"),
                            ("LTL", "Lithuanian Litas"),
                            ("LTT", "Lithuanian Talonas"),
                            ("LUL", "Luxembourg Financial Franc"),
                            ("LUC", "Luxembourgian Convertible Franc"),
                            ("LUF", "Luxembourgian Franc"),
                            ("MOP", "Macanese Pataca"),
                            ("MKD", "Macedonian Denar"),
                            ("MKN", "Macedonian Denar (1992–1993)"),
                            ("MGA", "Malagasy Ariary"),
                            ("MGF", "Malagasy Franc"),
                            ("MWK", "Malawian Kwacha"),
                            ("MYR", "Malaysian Ringgit"),
                            ("MVR", "Maldivian Rufiyaa"),
                            ("MVP", "Maldivian Rupee (1947–1981)"),
                            ("MLF", "Malian Franc"),
                            ("MTL", "Maltese Lira"),
                            ("MTP", "Maltese Pound"),
                            ("MRU", "Mauritanian Ouguiya"),
                            ("MRO", "Mauritanian Ouguiya (1973–2017)"),
                            ("MUR", "Mauritian Rupee"),
                            ("MXV", "Mexican Investment Unit"),
                            ("MXN", "Mexican Peso"),
                            ("MXP", "Mexican Silver Peso (1861–1992)"),
                            ("MDC", "Moldovan Cupon"),
                            ("MDL", "Moldovan Leu"),
                            ("MCF", "Monegasque Franc"),
                            ("MNT", "Mongolian Tugrik"),
                            ("MAD", "Moroccan Dirham"),
                            ("MAF", "Moroccan Franc"),
                            ("MZE", "Mozambican Escudo"),
                            ("MZN", "Mozambican Metical"),
                            ("MZM", "Mozambican Metical (1980–2006)"),
                            ("MMK", "Myanmar Kyat"),
                            ("NAD", "Namibian Dollar"),
                            ("NPR", "Nepalese Rupee"),
                            ("ANG", "Netherlands Antillean Guilder"),
                            ("TWD", "New Taiwan Dollar"),
                            ("NZD", "New Zealand Dollar"),
                            ("NIO", "Nicaraguan Córdoba"),
                            ("NIC", "Nicaraguan Córdoba (1988–1991)"),
                            ("NGN", "Nigerian Naira"),
                            ("KPW", "North Korean Won"),
                            ("NOK", "Norwegian Krone"),
                            ("OMR", "Omani Rial"),
                            ("PKR", "Pakistani Rupee"),
                            ("XPD", "Palladium"),
                            ("PAB", "Panamanian Balboa"),
                            ("PGK", "Papua New Guinean Kina"),
                            ("PYG", "Paraguayan Guarani"),
                            ("PEI", "Peruvian Inti"),
                            ("PEN", "Peruvian Sol"),
                            ("PES", "Peruvian Sol (1863–1965)"),
                            ("PHP", "Philippine Peso"),
                            ("XPT", "Platinum"),
                            ("PLN", "Polish Zloty"),
                            ("PLZ", "Polish Zloty (1950–1995)"),
                            ("PTE", "Portuguese Escudo"),
                            ("GWE", "Portuguese Guinea Escudo"),
                            ("QAR", "Qatari Rial"),
                            ("XRE", "RINET Funds"),
                            ("RHD", "Rhodesian Dollar"),
                            ("RON", "Romanian Leu"),
                            ("ROL", "Romanian Leu (1952–2006)"),
                            ("RUB", "Russian Ruble"),
                            ("RUR", "Russian Ruble (1991–1998)"),
                            ("RWF", "Rwandan Franc"),
                            ("SVC", "Salvadoran Colón"),
                            ("WST", "Samoan Tala"),
                            ("SAR", "Saudi Riyal"),
                            ("RSD", "Serbian Dinar"),
                            ("CSD", "Serbian Dinar (2002–2006)"),
                            ("SCR", "Seychellois Rupee"),
                            ("SLL", "Sierra Leonean Leone"),
                            ("XAG", "Silver"),
                            ("SGD", "Singapore Dollar"),
                            ("SKK", "Slovak Koruna"),
                            ("SIT", "Slovenian Tolar"),
                            ("SBD", "Solomon Islands Dollar"),
                            ("SOS", "Somali Shilling"),
                            ("ZAR", "South African Rand"),
                            ("ZAL", "South African Rand (financial)"),
                            ("KRH", "South Korean Hwan (1953–1962)"),
                            ("KRW", "South Korean Won"),
                            ("KRO", "South Korean Won (1945–1953)"),
                            ("SSP", "South Sudanese Pound"),
                            ("SUR", "Soviet Rouble"),
                            ("ESP", "Spanish Peseta"),
                            ("ESA", "Spanish Peseta (A account)"),
                            ("ESB", "Spanish Peseta (convertible account)"),
                            ("XDR", "Special Drawing Rights"),
                            ("LKR", "Sri Lankan Rupee"),
                            ("SHP", "St. Helena Pound"),
                            ("XSU", "Sucre"),
                            ("SDD", "Sudanese Dinar (1992–2007)"),
                            ("SDG", "Sudanese Pound"),
                            ("SDP", "Sudanese Pound (1957–1998)"),
                            ("SRD", "Surinamese Dollar"),
                            ("SRG", "Surinamese Guilder"),
                            ("SZL", "Swazi Lilangeni"),
                            ("SEK", "Swedish Krona"),
                            ("CHF", "Swiss Franc"),
                            ("SYP", "Syrian Pound"),
                            ("STN", "São Tomé & Príncipe Dobra"),
                            ("STD", "São Tomé & Príncipe Dobra (1977–2017)"),
                            ("TVD", "TVD"),
                            ("TJR", "Tajikistani Ruble"),
                            ("TJS", "Tajikistani Somoni"),
                            ("TZS", "Tanzanian Shilling"),
                            ("XTS", "Testing Currency Code"),
                            ("THB", "Thai Baht"),
                            (
                                "XXX",
                                "The codes assigned for transactions where no currency is involved",
                            ),
                            ("TPE", "Timorese Escudo"),
                            ("TOP", "Tongan Paʻanga"),
                            ("TTD", "Trinidad & Tobago Dollar"),
                            ("TND", "Tunisian Dinar"),
                            ("TRY", "Turkish Lira"),
                            ("TRL", "Turkish Lira (1922–2005)"),
                            ("TMT", "Turkmenistani Manat"),
                            ("TMM", "Turkmenistani Manat (1993–2009)"),
                            ("USD", "US Dollar"),
                            ("USN", "US Dollar (Next day)"),
                            ("USS", "US Dollar (Same day)"),
                            ("UGX", "Ugandan Shilling"),
                            ("UGS", "Ugandan Shilling (1966–1987)"),
                            ("UAH", "Ukrainian Hryvnia"),
                            ("UAK", "Ukrainian Karbovanets"),
                            ("AED", "United Arab Emirates Dirham"),
                            ("UYW", "Uruguayan Nominal Wage Index Unit"),
                            ("UYU", "Uruguayan Peso"),
                            ("UYP", "Uruguayan Peso (1975–1993)"),
                            ("UYI", "Uruguayan Peso (Indexed Units)"),
                            ("UZS", "Uzbekistani Som"),
                            ("VUV", "Vanuatu Vatu"),
                            ("VES", "Venezuelan Bolívar"),
                            ("VEB", "Venezuelan Bolívar (1871–2008)"),
                            ("VEF", "Venezuelan Bolívar (2008–2018)"),
                            ("VND", "Vietnamese Dong"),
                            ("VNN", "Vietnamese Dong (1978–1985)"),
                            ("CHE", "WIR Euro"),
                            ("CHW", "WIR Franc"),
                            ("XOF", "West African CFA Franc"),
                            ("YDD", "Yemeni Dinar"),
                            ("YER", "Yemeni Rial"),
                            ("YUN", "Yugoslavian Convertible Dinar (1990–1992)"),
                            ("YUD", "Yugoslavian Hard Dinar (1966–1990)"),
                            ("YUM", "Yugoslavian New Dinar (1994–2002)"),
                            ("YUR", "Yugoslavian Reformed Dinar (1992–1993)"),
                            ("ZWN", "ZWN"),
                            ("ZRN", "Zairean New Zaire (1993–1998)"),
                            ("ZRZ", "Zairean Zaire (1971–1993)"),
                            ("ZMW", "Zambian Kwacha"),
                            ("ZMK", "Zambian Kwacha (1968–2012)"),
                            ("ZWD", "Zimbabwean Dollar (1980–2008)"),
                            ("ZWR", "Zimbabwean Dollar (2008)"),
                            ("ZWL", "Zimbabwean Dollar (2009)"),
                        ],
                        default="USD",
                        editable=False,
                        max_length=3,
                        null=True,
                    ),
                ),
                (
                    "cost_due",
                    djmoney.models.fields.MoneyField(
                        decimal_places=2,
                        default_currency="USD",
                        max_digits=10,
                        null=True,
                    ),
                ),
                ("currency", models.CharField(default="USD", max_length=10)),
                ("issue_date", models.DateTimeField(auto_now=True, max_length=100)),
                ("customer_name", models.CharField(max_length=100)),
                ("customer_billing_id", models.CharField(max_length=40)),
                (
                    "invoice_pdf",
                    models.FileField(blank=True, null=True, upload_to="invoices/"),
                ),
                ("status", models.CharField(default="pending", max_length=10)),
                (
                    "line_items",
                    django.contrib.postgres.fields.ArrayField(
                        base_field=models.JSONField(), blank=True, null=True, size=None
                    ),
                ),
                (
                    "organization",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.organization",
                    ),
                ),
                (
                    "subscription",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.PROTECT,
                        to="metering_billing.subscription",
                    ),
                ),
            ],
        ),
        migrations.CreateModel(
            name="Event",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True,
                        primary_key=True,
                        serialize=False,
                        verbose_name="ID",
                    ),
                ),
                ("event_name", models.CharField(max_length=200)),
                ("time_created", models.DateTimeField()),
                ("properties", models.JSONField(blank=True, default=dict, null=True)),
                ("idempotency_id", models.CharField(max_length=255, unique=True)),
                (
                    "customer",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.customer",
                    ),
                ),
                (
                    "organization",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.organization",
                    ),
                ),
            ],
            options={
                "ordering": ["idempotency_id"],
            },
        ),
        migrations.AddField(
            model_name="customer",
            name="organization",
            field=models.ForeignKey(
                on_delete=django.db.models.deletion.CASCADE,
                to="metering_billing.organization",
            ),
        ),
        migrations.AddField(
            model_name="billingplan",
            name="organization",
            field=models.ForeignKey(
                on_delete=django.db.models.deletion.CASCADE,
                to="metering_billing.organization",
            ),
        ),
        migrations.AddField(
            model_name="billablemetric",
            name="organization",
            field=models.ForeignKey(
                on_delete=django.db.models.deletion.CASCADE,
                to="metering_billing.organization",
            ),
        ),
        migrations.CreateModel(
            name="APIToken",
            fields=[
                (
                    "id",
                    models.CharField(
                        editable=False,
                        max_length=150,
                        primary_key=True,
                        serialize=False,
                        unique=True,
                    ),
                ),
                ("prefix", models.CharField(editable=False, max_length=8, unique=True)),
                ("hashed_key", models.CharField(editable=False, max_length=150)),
                ("created", models.DateTimeField(auto_now_add=True, db_index=True)),
                (
                    "revoked",
                    models.BooleanField(
                        blank=True,
                        default=False,
                        help_text="If the API key is revoked, clients cannot use it anymore. (This cannot be undone.)",
                    ),
                ),
                (
                    "expiry_date",
                    models.DateTimeField(
                        blank=True,
                        help_text="Once API key expires, clients cannot use it anymore.",
                        null=True,
                        verbose_name="Expires",
                    ),
                ),
                ("name", models.CharField(default="latest_token", max_length=200)),
                (
                    "organization",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="metering_billing.organization",
                    ),
                ),
            ],
            options={
                "verbose_name": "API Token",
                "verbose_name_plural": "API Tokens",
            },
        ),
    ]

metering_billing/management/__init__.py METASEP

manage.py METASEP
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys


def main():
    """Run administrative tasks."""
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lotus.settings")
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)


if __name__ == "__main__":
    main()

metering_billing/views.py METASEP
import json
import math
import os

import dateutil.parser as parser
import stripe
from django.db import connection
from django.forms.models import model_to_dict
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest, JsonResponse
from django_q.tasks import async_task
from rest_framework import viewsets
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from lotus.settings import STRIPE_SECRET_KEY

from metering_billing.models import (
    BillingPlan,
    Customer,
    Event,
    Organization,
    PlanComponent,
    Subscription,
    User,
)

from .permissions import HasUserAPIKey
from .serializers import (
    BillingPlanSerializer,
    CustomerSerializer,
    EventSerializer,
    PlanComponentSerializer,
    SubscriptionSerializer,
)
from .tasks import generate_invoice

stripe.api_key = STRIPE_SECRET_KEY


class EventViewSet(viewsets.ModelViewSet):
    queryset = Event.objects.all()
    serializer_class = EventSerializer


class SubscriptionViewSet(viewsets.ModelViewSet):
    queryset = Subscription.objects.all()
    serializer_class = SubscriptionSerializer


class PlansView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):

        organization = request.user.organization_set.first()
        plans = BillingPlan.objects.filter(organization=organization)

        plans_list = []

        for plan in plans:
            plan_breakdown = {}
            plan_data = BillingPlanSerializer(plan).data
            plan_breakdown["name"] = plan_data["name"]

            components = PlanComponent.objects.filter(billing_plan=plan)

            components_list = []
            for component in components:
                component_breakdown = {}
                component_data = PlanComponentSerializer(component).data
                component_breakdown["free_metric_quantity"] = int(
                    component_data["free_metric_quantity"]
                )
                component_breakdown["cost_per_metric"] = component_data[
                    "cost_per_metric"
                ]
                component_breakdown["unit_per_cost"] = component_data[
                    "metric_amount_per_cost"
                ]
                component_breakdown[
                    "metric_name"
                ] = component.billable_metric.event_name
                component_breakdown[
                    "aggregation_type"
                ] = component.billable_metric.aggregation_type.upper()
                component_breakdown[
                    "property_name"
                ] = component.billable_metric.property_name

                components_list.append(component_breakdown)
            plan_breakdown["components"] = components_list
            plan_breakdown["billing_interval"] = plan_data["interval"]
            plan_breakdown["description"] = plan_data["description"]
            plan_breakdown["flat_rate"] = plan_data["flat_rate"]

            plans_list.append(plan_breakdown)

        return JsonResponse(plans_list, safe=False)

    def post(self, request, format=None):
        pass


class SubscriptionView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        """
        List active subscriptions. If customer_id is provided, only return subscriptions for that customer.
        """

        organization = request.user.organization_set.first()
        if "customer_id" in request.query_params:
            customer_id = request.query_params["customer_id"]
            try:
                customer = Customer.objects.get(
                    customer_id=customer_id, organization=organization
                )
            except Customer.DoesNotExist:
                return HttpResponseBadRequest("Customer does not exist")
            subscriptions = Subscription.objects.filter(
                customer=customer, status="active"
            )
            serializer = SubscriptionSerializer(subscriptions, many=True)
            return Response(serializer.data)
        else:
            subscriptions = Subscription.objects.filter(status="active")
            serializer = SubscriptionSerializer(subscriptions, many=True)
            return Response(serializer.data)

    def post(self, request, format=None):
        """
        Create a new subscription, joining a customer and a plan.
        """
        data = request.data
        organization = request.user.organization_set.first()

        customer_qs = Customer.objects.filter(
            customer_id=data["customer_id"], organization=organization
        )
        start_date = parser.parse(data["start_date"])
        if len(customer_qs) < 1:
            return Response(
                {
                    "error": "Customer with custmer_id {} does not exist".format(
                        data["custmer_id"]
                    )
                },
                status=400,
            )
        else:
            customer = customer_qs[0]

        organization_qs = Organization.objects.filter(id=data["organization_id"])
        if len(organization_qs) < 1:
            return Response(
                {
                    "error": "Organization with organization_id {} does not exist".format(
                        data["organization_id"]
                    )
                },
                status=400,
            )
        else:
            organization = organization_qs[0]

        plan_qs = BillingPlan.objects.filter(
            plan_id=data["plan_id"], organization=organization
        )
        if len(plan_qs) < 1:
            return Response(
                {
                    "error": "Plan with plan_id {} does not exist".format(
                        data["plan_id"]
                    )
                },
                status=400,
            )
        else:
            plan = plan_qs[0]
            end_date = plan.subscription_end_date(start_date)

        subscription = Subscription.objects.create(
            organization=organization,
            customer=customer,
            start_date=start_date,
            end_date=end_date,
            billing_plan=plan,
            status="active",
        )
        subscription.save()

        serializer_context = {
            "request": request,
        }

        return Response("Subscription Created", status=201)


class CustomerView(APIView):

    permission_classes = [IsAuthenticated | HasUserAPIKey]

    def get(self, request, format=None):
        """
        Return a list of all customers.
        """
        organization = request.user.organization_set.first()
        customers = Customer.objects.filter(organization=organization)
        serializer = CustomerSerializer(customers, many=True)
        customer_list = []
        for customer in customers:
            serializer = CustomerSerializer(customer)
            cust_data = serializer.data
            cust_data["plan"] = {
                "name": customer.get_billing_plan_name(),
                "color": "green",
            }
            del cust_data["organization"]
            customer_list.append(cust_data)
        return Response(customer_list)

    def post(self, request, format=None):
        """
        Create a new customer.
        """
        organization = request.user.organization_set.first()
        request.data["organization"] = organization
        serializer = CustomerSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=201)
        return Response(serializer.errors, status=400)


class UsageView(APIView):

    permission_classes = [IsAuthenticated | HasUserAPIKey]

    def get(self, request, format=None):
        """
        Return current usage for a customer during a given billing period.
        """
        customer_id = request.query_params["customer_id"]
        customer_qs = Customer.objects.filter(customer_id=customer_id)

        if len(customer_qs) < 1:
            return Response(
                {
                    "error": "Customer with customer_id {} does not exist".format(
                        customer_id
                    )
                },
                status=400,
            )
        else:
            customer = customer_qs[0]
        customer_subscriptions = Subscription.objects.filter(
            customer=customer, status="active"
        )

        usage_summary = {}
        for subscription in customer_subscriptions:

            plan = subscription.billing_plan
            flat_rate = int(plan.flat_rate.amount)
            plan_start_timestamp = subscription.start_date
            plan_end_timestamp = subscription.end_date

            plan_components_qs = PlanComponent.objects.filter(billing_plan=plan.id)
            subscription_cost = 0
            plan_components_summary = {}
            # For each component of the plan, calculate usage/cost
            for plan_component in plan_components_qs:
                billable_metric = plan_component.billable_metric
                event_name = billable_metric.event_name
                aggregation_type = billable_metric.aggregation_type
                subtotal_usage = 0.0
                subtotal_cost = 0.0

                events = Event.objects.filter(
                    event_name=event_name,
                    time_created__gte=plan_start_timestamp,
                    time_created__lte=plan_end_timestamp,
                )

                if aggregation_type == "count":
                    subtotal_usage = len(events) - plan_component.free_metric_quantity
                    metric_batches = math.ceil(
                        subtotal_usage / plan_component.metric_amount_per_cost
                    )
                elif aggregation_type == "sum":
                    property_name = billable_metric.property_name
                    for event in events:
                        properties_dict = event.properties
                        if property_name in properties_dict:
                            subtotal_usage += float(properties_dict[property_name])
                    subtotal_usage -= plan_component.free_metric_quantity
                    metric_batches = math.ceil(
                        subtotal_usage / plan_component.metric_amount_per_cost
                    )

                elif aggregation_type == "max":
                    property_name = billable_metric.property_name
                    for event in events:
                        properties_dict = event.properties
                        if property_name in properties_dict:
                            subtotal_usage = max(
                                subtotal_usage, float(properties_dict[property_name])
                            )
                        metric_batches = subtotal_usage
                subtotal_cost = int(
                    (metric_batches * plan_component.cost_per_metric).amount
                )
                subscription_cost += subtotal_cost

                subtotal_cost_string = "$" + str(subtotal_cost)
                plan_components_summary[str(plan_component)] = {
                    "cost": subtotal_cost_string,
                    "usage": str(subtotal_usage),
                    "free_usage_left": str(
                        max(plan_component.free_metric_quantity - subtotal_usage, 0)
                    ),
                }

            usage_summary[plan.name] = {
                "total_usage_cost": "$" + str(subscription_cost),
                "flat_rate_cost": "$" + str(flat_rate),
                "components": plan_components_summary,
                "current_amount_due": "$" + str(subscription_cost + flat_rate),
                "billing_start_date": plan_start_timestamp,
                "billing_end_date": plan_end_timestamp,
            }

        usage_summary["# of Active Subscriptions"] = len(usage_summary)
        return Response(usage_summary)


def import_stripe_customers(organization):
    """
    If customer exists in Stripe and also exists in Lotus (compared by matching names), then update the customer's payment provider ID from Stripe.
    """

    stripe_customers_response = stripe.Customer.list(
        stripe_account=organization.stripe_id
    )

    for stripe_customer in stripe_customers_response.auto_paging_iter():
        try:
            customer = Customer.objects.get(name=stripe_customer["name"])
            customer.payment_provider_id = stripe_customer["id"]
            customer.save()
        except Customer.DoesNotExist:
            pass


def issue_stripe_payment_intent(invoice):

    cost_due = int(invoice.cost_due * 100)
    currency = (invoice.currency).lower()

    stripe.PaymentIntent.create(
        amount=cost_due,
        currency=currency,
        payment_method_types=["card"],
        stripe_account=invoice.organization.stripe_id,
    )


def retrive_stripe_payment_intent(invoice):
    payment_intent = stripe.PaymentIntent.retrieve(
        invoice.payment_intent_id,
        stripe_account=invoice.organization.stripe_id,
    )
    return payment_intent


class InitializeStripeView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, format=None):
        """
        Check to see if user has connected their Stripe account.
        """

        organization = request.user.organization_set.first()

        stripe_id = organization.stripe_id

        if stripe_id and len(stripe_id) > 0:
            return JsonResponse({"connected": True})
        else:
            return JsonResponse({"connected": False})

    def post(self, request, format=None):
        """
        Initialize Stripe after user inputs an API key.
        """

        data = request.data

        if data is None:
            return JsonResponse({"details": "No data provided"}, status=400)

        user = request.user
        organization = user.organization_set.first()
        stripe_code = data["authorization_code"]

        try:
            response = stripe.OAuth.token(
                grant_type="authorization_code",
                code=stripe_code,
            )
        except:
            return JsonResponse(
                {"success": False, "details": "Invalid authorization code"}, status=400
            )

        if "error" in response:
            return JsonResponse(
                {"success": False, "details": response["error"]}, status=400
            )

        connected_account_id = response["stripe_user_id"]

        organization.stripe_id = connected_account_id

        import_stripe_customers(organization)

        organization.save()

        return JsonResponse({"Success": True})

metering_billing/track.py METASEP
from ast import Or
import base64
import json
from re import S
from typing import Dict, List, Union
from urllib.parse import urlparse

from django.db import IntegrityError
from django.http import HttpRequest, HttpResponse, HttpResponseBadRequest, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, permission_classes
from rest_framework.parsers import JSONParser
from rest_framework.permissions import IsAuthenticated

from metering_billing.models import BillingPlan, Customer, Event, Organization, APIToken

from .permissions import HasUserAPIKey


def load_event(request: HttpRequest) -> Union[None, Dict]:
    """
    Loads an event from the request body.
    """

    if request.content_type == "application/json":
        try:
            event_data = json.loads(request.body)
            return event_data
        except json.JSONDecodeError as e:
            print(e)
            # if not, it's probably base64 encoded from other libraries
            event_data = json.loads(
                base64.b64decode(request + "===")
                .decode("utf8", "surrogatepass")
                .encode("utf-16", "surrogatepass")
            )
    else:
        event_data = request.body.decode("utf8")
    if event_data is None:
        return None

    return event_data


def ingest_event(request, data: dict, customer: Customer, organization) -> None:

    idempotency_id_query = Event.objects.filter(
        idempotency_id=data["idempotency_id"]
    ).count()

    if idempotency_id_query > 0:
        return JsonResponse(
            {"detail": "This event record already exists", "status": "Failure"},
            status=409,
        )

    db_event = Event.objects.create(
        organization=organization,
        event_name=data["event_name"],
        idempotency_id=data["idempotency_id"],
        customer=customer,
        time_created=data["time_created"],
    )
    if "properties" in data:
        db_event.properties = data["properties"]
    db_event.save()
    return JsonResponse({"status": "Success"}, status=200)


@csrf_exempt
@permission_classes((HasUserAPIKey))
def track_event(request):
    # Find the associated organization, need to move to middleware/auth
    validator = HasUserAPIKey()
    key = validator.get_key(request)
    api_key = APIToken.objects.get_from_key(key)
    organization = api_key.organization

    data = load_event(request)
    if not data:
        return HttpResponseBadRequest("No data provided")
    customer_id = data["customer_id"]
    try:
        customer = Customer.objects.get(customer_id=customer_id)
    except Customer.DoesNotExist:
        return HttpResponseBadRequest("Customer does not exist")

    if isinstance(data, list):
        for i in data:
            try:
                return ingest_event(
                    request=request,
                    data=i,
                    customer=customer,
                    organization=organization,
                )
            except KeyError:
                return JsonResponse(
                    {
                        "code": "validation",
                        "message": "You need to set a distinct_id.",
                        "item": data,
                    },
                    status=400,
                )
    else:
        return ingest_event(
            request=request, data=data, customer=customer, organization=organization
        )

metering_billing/tasks.py METASEP
from __future__ import absolute_import, unicode_literals

from datetime import datetime

from lotus.celery import app
from metering_billing.models import Subscription

from .generate_invoice import generate_invoice


@app.task
def calculate_invoice():
    ending_subscriptions = Subscription.objects.filter(
        status="active", end_date__lte=datetime.now()
    )
    for subscription in ending_subscriptions:
        # Generate the invoice
        try:
            generate_invoice(subscription)
        except Exception as e:
            print(e)
            print("Error generating invoice for subscription {}".format(subscription))
            continue
        # Renew the subscription
        subscription.start_date = datetime.now()
        subscription.end_date = subscription.billing_plan.subscription_end_date(
            subscription.start_date
        )
        subscription.save()

metering_billing/serializers.py METASEP
from rest_framework import serializers

from metering_billing.models import (
    BillingPlan,
    Customer,
    Event,
    Invoice,
    PlanComponent,
    Subscription,
    User,
)


class InvoiceSerializer(serializers.ModelSerializer):
    class Meta:
        model = Invoice
        fields = "__all__"


class EventSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Event
        fields = (
            "organization",
            "customer",
            "event_name",
            "time_created",
            "properties",
            "idempotency_id",
        )


class CustomerSerializer(serializers.ModelSerializer):
    class Meta:
        model = Customer
        fields = (
            "organization",
            "name",
            "customer_id",
            "billing_id",
            "balance",
        )


class SubscriptionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Subscription
        fields = (
            "organization",
            "customer",
            "billing_plan",
            "start_date",
            "end_date",
            "status",
        )


class BillingPlanSerializer(serializers.ModelSerializer):
    class Meta:
        model = BillingPlan
        fields = "__all__"


class PlanComponentSerializer(serializers.ModelSerializer):
    class Meta:
        model = PlanComponent
        fields = "__all__"


class CurrentUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ("username", "password")

metering_billing/permissions.py METASEP
from rest_framework_api_key.permissions import BaseHasAPIKey

from metering_billing.models import APIToken


class HasUserAPIKey(BaseHasAPIKey):
    model = APIToken

metering_billing/models.py METASEP
import uuid
from email.mime import base
from operator import mod

from dateutil.parser import isoparse
from dateutil.relativedelta import relativedelta
from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from djmoney.models.fields import MoneyField
from model_utils import Choices
from moneyed import Money
from rest_framework_api_key.models import AbstractAPIKey


PAYMENT_PLANS = Choices(
    ("self_hosted_free", _("Self-Hosted Free")),
    ("cloud", _("Cloud")),
    ("self_hosted_enterprise", _("Self-Hosted Enterprise")),
)
# Create your models here.
class User(AbstractUser):

    company_name = models.CharField(max_length=200, default=" ")


class Organization(models.Model):

    users = models.ManyToManyField(User, blank=True)
    company_name = models.CharField(max_length=100, default=" ")
    stripe_id = models.CharField(max_length=110, default="", blank=True)
    payment_plan = models.CharField(
        max_length=40, choices=PAYMENT_PLANS, default=PAYMENT_PLANS.self_hosted_free
    )
    id = models.CharField(
        max_length=40, unique=True, default=uuid.uuid4, primary_key=True
    )
    created_on = models.DateField(auto_now_add=True)


class Customer(models.Model):

    """
    Customer Model

    This model represents a customer.

    Attributes:
        name (str): The name of the customer.
        customer_id (str): The external id of the customer in the backend system.
        billing_id (str): The billing id of the customer, internal to Lotus.
    """

    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=False)
    name = models.CharField(max_length=100)
    customer_id = models.CharField(max_length=40, unique=True)
    billing_id = models.CharField(max_length=40, default=uuid.uuid4)

    # balance (in cents) in currency that a customer currently has during this billing period, negative means they owe money, postive is a credit towards their invoice
    balance = MoneyField(
        default=0, max_digits=10, decimal_places=2, default_currency="USD"
    )
    currency = models.CharField(max_length=3, default="USD")

    payment_provider_id = models.CharField(max_length=50, null=True, blank=True)

    properties = models.JSONField(default=dict, blank=True, null=True)

    def __str__(self) -> str:
        return str(self.name) + " " + str(self.customer_id)

    def get_billing_plan_name(self) -> str:
        subscription_object = Subscription.objects.filter(customer=self).first()
        if subscription_object is None:
            return "None"
        return subscription_object.billing_plan.get_plan_name()


class Event(models.Model):
    """
    Event object. An explanation of the Event's fields follows:
    event_name: The type of event that occurred.
    time_created: The time at which the event occurred.
    customer: The customer that the event occurred to.
    idempotency_id: A unique identifier for the event.
    """

    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=False)

    customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=False)
    event_name = models.CharField(max_length=200, null=False)
    time_created: models.DateTimeField = models.DateTimeField()
    properties: models.JSONField = models.JSONField(default=dict, blank=True, null=True)
    idempotency_id: models.CharField = models.CharField(max_length=255, unique=True)

    class Meta:
        ordering = ["idempotency_id"]

    def __str__(self):
        return str(self.event_name) + "-" + str(self.idempotency_id)


class BillableMetric(models.Model):
    class AGGREGATION_TYPES(object):
        COUNT = "count"
        SUM = "sum"
        MAX = "max"

    AGGREGATION_CHOICES = Choices(
        (AGGREGATION_TYPES.COUNT, _("Count")),
        (AGGREGATION_TYPES.SUM, _("Sum")),
        (AGGREGATION_TYPES.MAX, _("Max")),
    )
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=False)
    event_name = models.CharField(max_length=200, null=False)
    property_name = models.CharField(max_length=200, null=True)
    aggregation_type = models.CharField(
        max_length=10,
        choices=AGGREGATION_CHOICES,
        default=AGGREGATION_CHOICES.count,
    )

    def __str__(self):
        return (
            str(self.aggregation_type)
            + " of "
            + str(self.property_name)
            + " : "
            + str(self.event_name)
        )

    def get_aggregation_type(self):
        return self.aggregation_type


class BillingPlan(models.Model):
    """
    Billing_ID: Id for this specific plan
    time_created: self-explanatory
    currency: self-explanatory
    interval: determines whether plan charges weekly, monthly, or yearly
    flat_rate: amount to charge every week, month, or year (depending on choice of interval)
    billable_metrics: a json containing a list of billable_metrics objects
    """

    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=False)
    plan_id = models.CharField(
        max_length=36, blank=True, unique=True, default=uuid.uuid4
    )

    time_created: models.DateTimeField = models.DateTimeField(auto_now=True)
    currency = models.CharField(max_length=30, default="USD")  # 30 is arbitrary

    INTERVAL_CHOICES = Choices(
        ("week", _("Week")),
        ("month", _("Month")),
        ("year", _("Year")),
    )

    interval = models.CharField(
        max_length=5,
        choices=INTERVAL_CHOICES,
        default=INTERVAL_CHOICES.month,
    )

    flat_rate = MoneyField(
        decimal_places=2, max_digits=8, default_currency="USD", default=0.0
    )
    pay_in_advance = models.BooleanField(default=False)

    name = models.CharField(max_length=200, default=" ")
    description = models.CharField(max_length=256, default=" ", blank=True)

    def subscription_end_date(self, start_date):
        start_date_parsed = start_date
        if self.interval == "week":
            return start_date_parsed + relativedelta(weeks=+1)
        elif self.interval == "month":
            return start_date_parsed + relativedelta(months=+1)
        elif self.interval == "year":
            return start_date_parsed + relativedelta(years=+1)
        else:
            print("none")
            return None

    def get_usage_cost_count_aggregation(self, num_events):
        return max(0, self.metric_amount * (self.starter_metric_quantity - num_events))

    def get_plan_name(self):
        return self.name

    def __str__(self) -> str:
        return str(self.name) + ":" + str(self.plan_id)


class PlanComponent(models.Model):
    billing_plan = models.ForeignKey(BillingPlan, on_delete=models.CASCADE)
    billable_metric = models.ForeignKey(BillableMetric, on_delete=models.CASCADE)

    free_metric_quantity = models.IntegerField(default=0)
    cost_per_metric = MoneyField(
        decimal_places=10, max_digits=14, default_currency="USD"
    )
    metric_amount_per_cost = models.IntegerField(default=1)

    def __str__(self) -> str:
        return str(self.billable_metric)


class Subscription(models.Model):
    """
    Subscription object. An explanation of the Subscription's fields follows:
    customer: The customer that the subscription belongs to.
    plan_name: The name of the plan that the subscription is for.
    start_date: The date at which the subscription started.
    end_date: The date at which the subscription will end.
    status: The status of the subscription, active or ended.
    """

    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=False)
    STATUSES = Choices(
        ("active", _("Active")),
        ("ended", _("Ended")),
    )

    id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4)

    customer: models.ForeignKey = models.ForeignKey(Customer, on_delete=models.CASCADE)
    billing_plan = models.ForeignKey(BillingPlan, on_delete=models.CASCADE)
    start_date = models.DateField()
    end_date = models.DateField()
    status = models.CharField(max_length=6, choices=STATUSES, default=STATUSES.active)

    class Meta:
        unique_together = (
            "start_date",
            "end_date",
        )

    def __str__(self):
        return str(self.customer) + " " + str(self.billing_plan)


class Invoice(models.Model):

    id = models.CharField(primary_key=True, max_length=36, default=uuid.uuid4)
    cost_due = models.IntegerField(default=0)
    currency = models.CharField(max_length=10, default="USD")
    issue_date = models.DateTimeField(max_length=100, auto_now=True)
    organization = models.ForeignKey(Organization, on_delete=models.CASCADE, null=False)
    customer_name = models.CharField(max_length=100)
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    customer_billing_id = models.CharField(max_length=40)

    invoice_pdf = models.FileField(upload_to="invoices/", null=True, blank=True)

    subscription = models.ForeignKey(Subscription, on_delete=models.PROTECT)

    status = models.CharField(max_length=10, default="pending")

    line_items = ArrayField(base_field=models.JSONField(), null=True, blank=True)


class APIToken(AbstractAPIKey):
    organization = models.ForeignKey(
        Organization, on_delete=models.CASCADE, related_name="api_keys"
    )
    name = models.CharField(max_length=200, default="latest_token")

    def __str__(self):
        return str(self.name) + " " + str(self.organization)

    class Meta(AbstractAPIKey.Meta):
        verbose_name = "API Token"
        verbose_name_plural = "API Tokens"


@receiver(post_save, sender=Organization)
def create_token(sender, instance, created=False, **kwargs):
    if created:
        APIToken.objects.create(organization=instance)

metering_billing/generate_invoice.py METASEP
from metering_billing.models import Customer, Event, Invoice, Subscription


def generate_invoice(subscription):
    """
    Generate an invoice for a subscription.
    """
    # Get the customer
    customer_obj = subscription.customer

    # Get the billing plan
    billing_plan = subscription.billing_plan

    billable_metric = billing_plan.billable_metric
    aggregation_type = billable_metric.get_aggregation_type()

    # Get the events for the subscription
    events = Event.objects.all().filter(event_name=billable_metric.event_name)

    if aggregation_type == "count":
        # Get the total number of events
        num_events = len(events)
        usage_cost = billing_plan.get_usage_cost_count_aggregation(num_events)

    else:
        usage_cost = 0.0

    # Get the total cost of the subscription
    total_cost = billing_plan.flat_rate + usage_cost

    # Create the invoice
    invoice = Invoice(
        cost_due=total_cost,
        currency=billing_plan.currency,
        issue_date=subscription.end_date,
        subscription=subscription,
        customer_name=customer_obj.name,
        customer_billing_id=customer_obj.billing_id,
    )
    invoice.save()

    # Create the events for the invoice
    return invoice

metering_billing/auth_views.py METASEP
import json

from django.contrib.auth import authenticate, login, logout
from django.http import JsonResponse
from django.views.decorators.csrf import ensure_csrf_cookie
from django.views.decorators.http import require_POST

from .serializers import CurrentUserSerializer


@require_POST
def login_view(request):

    data = json.loads(request.body)

    if data is None:
        return JsonResponse({"detail": "No data provided."}, status=400)

    username = data.get("username")
    password = data.get("password")

    if username is None or password is None:
        return JsonResponse(
            {"detail": "Please provide username and password."}, status=400
        )

    user = authenticate(username=username, password=password)

    if user is None:
        return JsonResponse({"detail": "Invalid credentials."}, status=400)

    login(request, user)
    return JsonResponse({"detail": "Successfully logged in."})


def logout_view(request):
    if not request.user.is_authenticated:
        return JsonResponse({"detail": "You're not logged in."}, status=400)

    logout(request)
    return JsonResponse({"detail": "Successfully logged out."})


@ensure_csrf_cookie
def session_view(request):

    if not request.user.is_authenticated:

        return JsonResponse({"isAuthenticated": False})

    return JsonResponse({"isAuthenticated": True})


def whoami_view(request):
    if not request.user.is_authenticated:
        return JsonResponse({"isAuthenticated": False})

    return JsonResponse({"username": request.user.username})

metering_billing/apps.py METASEP
from django.apps import AppConfig


class MeteringBillingConfig(AppConfig):
    default_auto_field = "django.db.models.BigAutoField"
    name = "metering_billing"

metering_billing/admin.py METASEP
from django.contrib import admin
from rest_framework_api_key.admin import APIKeyModelAdmin
from rest_framework_api_key.models import APIKey

from .models import (
    APIToken,
    BillableMetric,
    BillingPlan,
    Customer,
    Event,
    Invoice,
    Organization,
    PlanComponent,
    Subscription,
    User,
)

# Register your models here.
admin.site.register(Customer)
admin.site.register(Event)
admin.site.register(BillingPlan)
admin.site.register(Subscription)
admin.site.register(Invoice)
admin.site.register(BillableMetric)
admin.site.register(PlanComponent)
admin.site.register(User)
admin.site.register(Organization)

admin.site.unregister(APIKey)


@admin.register(APIToken)
class UserAPIKeyModelAdmin(APIKeyModelAdmin):
    pass

metering_billing/__init__.py METASEP

metering_billing/tests/__init__.py METASEP

metering_billing/tests/test_customer.py METASEP

import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
        assert len(get_customers_in_org(setup_dict["org"])) == 1

    def test_session_auth_can_create_customer_nonempty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false, authenticated=true
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        response = client = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0
        assert len(get_customers_in_org(setup_dict["org"])) == num_customers + 1

    def test_user_org_and_api_key_different_reject_creation(
infile
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
        assert len(get_customers_in_org(setup_dict["org"])) == 1

    def test_session_auth_can_create_customer_nonempty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false, authenticated=true
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        response = client = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0
        assert len(get_customers_in_org(setup_dict["org"])) == num_customers + 1

    def test_user_org_and_api_key_different_reject_creation(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers user_org_and_api_key_org_different = True
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
infile
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
infile
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
infile
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

infile
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

common
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
        assert len(get_customers_in_org(setup_dict["org"])) == 1

    def test_session_auth_can_create_customer_nonempty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false, authenticated=true
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

common
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
common
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
        assert len(get_customers_in_org(setup_dict["org"])) == 1

non_informative
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
        assert len(get_customers_in_org(setup_dict["org"])) == 1

    def test_session_auth_can_create_customer_nonempty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
non_informative
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
non_informative
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
        assert len(get_customers_in_org(setup_dict["org"])) == 1

    def test_session_auth_can_create_customer_nonempty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false, authenticated=true
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        response = client = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0
        assert len(get_customers_in_org(setup_dict["org"])) == num_customers + 1

    def test_user_org_and_api_key_different_reject_creation(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers user_org_and_api_key_org_different = True
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )
non_informative
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
random
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
random
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
random
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
random
import json

import pytest
from django.core.serializers.json import DjangoJSONEncoder
from django.urls import reverse
from metering_billing.models import User
from model_bakery import baker
from rest_framework import status
from rest_framework.test import APIClient


@pytest.fixture
def customer_test_common_setup(
    generate_org_and_api_key,
    add_customers_to_org,
    add_users_to_org,
    api_client_with_api_key_auth,
):
    def do_customer_test_common_setup(
        *, num_customers, auth_method, user_org_and_api_key_org_different
    ):
        # set up organizations and api keys
        org, key = generate_org_and_api_key()
        org2, key2 = generate_org_and_api_key()
        setup_dict = {
            "org": org,
            "key": key,
            "org2": org2,
            "key2": key2,
        }
        # set up the client with the appropriate api key spec
        if auth_method == "api_key":
            client = api_client_with_api_key_auth(key)
        elif auth_method == "session_auth":
            client = APIClient()
            (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        else:
            client = api_client_with_api_key_auth(key)
            if user_org_and_api_key_org_different:
                (user,) = add_users_to_org(org2, n=1)
            else:
                (user,) = add_users_to_org(org, n=1)
            client.force_authenticate(user=user)
            setup_dict["user"] = user
        setup_dict["client"] = client

        # set up number of customers
        if num_customers > 0:
            add_customers_to_org(org, n=num_customers)
            add_customers_to_org(org2, n=num_customers)

        return setup_dict

    return do_customer_test_common_setup


@pytest.mark.django_db
class TestGetCustomers:
    """Testing the GET of Customer endpoint:
    GET: Return list of customers associated with the organization with API key.
        partitions:
        auth_method: api_key, session_auth, both
        num_customers: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_access_customers_empty(self, customer_test_common_setup):
        # covers num_customers=0, auth_method=api_key, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=0,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_session_auth_can_access_customers_multiple(
        self, customer_test_common_setup
    ):
        # covers num customers > 0, auth_method=session_auth
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_200_OK
        assert len(response.data) == num_customers

    def test_user_org_and_api_key_different_reject_access(
        self, customer_test_common_setup
    ):
        # covers user_org_and_api_key_org_different = true
        num_customers = 3
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="both",
            user_org_and_api_key_org_different=True,
        )

        payload = {}
        response = setup_dict["client"].get(reverse("customer"), payload)

        assert response.status_code == status.HTTP_406_NOT_ACCEPTABLE


@pytest.fixture
def insert_customer_payload():
    payload = {
        "name": "test_customer",
        "customer_id": "test_customer_id",
        "billing_id": "test_billing_id",
        "balance": 30,
        "currency": "USD",
        "payment_provider_id": "test_payment_provider_id",
        "properties": {},
    }
    return payload


@pytest.mark.django_db
class TestInsertCustomer:
    """Testing the POST of Customer endpoint:
    POST: Return list of customers associated with the organization with API key / user.
    partitions:
        auth_method: api_key, session_auth, both
        num_customers_before_insert: 0, >0
        user_org_and_api_key_org_different: true, false
    """

    def test_api_key_can_create_customer_empty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false
        num_customers = 0
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="api_key",
            user_org_and_api_key_org_different=False,
        )

        response = setup_dict["client"].post(
            reverse("customer"),
            data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),
            content_type="application/json",
        )

        assert response.status_code == status.HTTP_201_CREATED
        assert len(response.data) > 0  # check that the response is not empty
        assert len(get_customers_in_org(setup_dict["org"])) == 1

    def test_session_auth_can_create_customer_nonempty_before(
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org
    ):
        # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false, authenticated=true
        num_customers = 5
        setup_dict = customer_test_common_setup(
            num_customers=num_customers,
            auth_method="session_auth",
            user_org_and_api_key_org_different=False,
        )

        response = client = setup_dict["client"].post(
random
        self, customer_test_common_setup, insert_customer_payload, get_customers_in_org             data=json.dumps(insert_customer_payload, cls=DjangoJSONEncoder),         self, customer_test_common_setup         setup_dict = customer_test_common_setup(     return do_customer_test_common_setup         response = setup_dict["client"].post(         response = client = setup_dict["client"].post(         response = setup_dict["client"].get(reverse("customer"), payload)     def test_session_auth_can_create_customer_nonempty_before(         # covers num_customers_before_insert = 0, has_org_api_key=true, user_in_org=true, user_org_and_api_key_org_different=false, authenticated=true from django.urls import reverse         org2, key2 = generate_org_and_api_key()                 (user,) = add_users_to_org(org2, n=1)             setup_dict["user"] = user         assert len(get_customers_in_org(setup_dict["org"])) == 1             reverse("customer"),