export class DefaultHasManyEntityCrudRepository<
  TargetEntity extends Entity,
  TargetID,
  TargetRepository extends EntityCrudRepository<TargetEntity, TargetID>
> implements HasManyRepository<TargetEntity> {
  /**
   * Constructor of DefaultHasManyEntityCrudRepository
   * @param targetRepository the related target model repository instance
   * @param constraint the key value pair representing foreign key name to constrain
   * the target repository instance
   */
  constructor(
    public targetRepository: TargetRepository,
    public constraint: AnyObject,
  ) {}

  async create(
    targetModelData: Partial<TargetEntity>,
    options?: Options,
  ): Promise<TargetEntity> {
    return await this.targetRepository.create(
      constrainDataObject(targetModelData, this.constraint),
      options,
    );
  }

  async find(filter?: Filter, options?: Options): Promise<TargetEntity[]> {
    return await this.targetRepository.find(
      constrainFilter(filter, this.constraint),
      options,
    );
  }

  async delete(where?: Where, options?: Options): Promise<number> {
    return await this.targetRepository.deleteAll(
      constrainWhere(where, this.constraint),
      options,
    );
  }

  async patch(
    dataObject: Partial<TargetEntity>,
    where?: Where,
    options?: Options,
  ): Promise<number> {
    return await this.targetRepository.updateAll(
      constrainDataObject(dataObject, this.constraint),
      constrainWhere(where, this.constraint),
      options,
    );
  }
}


/**
 * CRUD operations for a target repository of a HasMany relation
 */
export interface HasManyRepository<Target extends Entity> {
  /**
   * Create a target model instance
   * @param targetModelData The target model data
   * @param options Options for the operation
   * @returns A promise which resolves to the newly created target model instance
   */
  create(targetModelData: Partial<Target>, options?: Options): Promise<Target>;
  /**
   * Find target model instance(s)
   * @param Filter A filter object for where, order, limit, etc.
   * @param options Options for the operation
   * @returns A promise which resolves with the found target instance(s)
   */
  find(filter?: Filter, options?: Options): Promise<Target[]>;
  /**
   * Delete multiple target model instances
   * @param where Instances within the where scope are deleted
   * @param options
   * @returns A promise which resolves the deleted target model instances
   */
  delete(where?: Where, options?: Options): Promise<number>;
  /**
   * Patch multiple target model instances
   * @param dataObject The fields and their new values to patch
   * @param where Instances within the where scope are patched
   * @param options
   * @returns A promise which resolves the patched target model instances
   */
  patch(
    dataObject: DataObject<Target>,
    where?: Where,
    options?: Options,
  ): Promise<number>;
}


async create(
    targetModelData: Partial<TargetEntity>,
    options?: Options,
  ): Promise<TargetEntity> {
    return await this.targetRepository.create(
      constrainDataObject(targetModelData, this.constraint),
      options,
    );
  }


async find(filter?: Filter, options?: Options): Promise<TargetEntity[]> {
    return await this.targetRepository.find(
      constrainFilter(filter, this.constraint),
      options,
    );
  }


async delete(where?: Where, options?: Options): Promise<number> {
    return await this.targetRepository.deleteAll(
      constrainWhere(where, this.constraint),
      options,
    );
  }


async patch(
    dataObject: Partial<TargetEntity>,
    where?: Where,
    options?: Options,
  ): Promise<number> {
    return await this.targetRepository.updateAll(
      constrainDataObject(dataObject, this.constraint),
      constrainWhere(where, this.constraint),
      options,
    );
  }


(num: number) => Math.round(num)


() => (
  <ConsoleLineChart data={data}


(): Keypair => {
        return new Keypair(this.privKey.copy())
    }


(): PrivKey => {
        return new PrivKey(bigInt(this.rawPrivKey.toString()))
    }


() => {
        return formatPrivKeyForBabyJub(this.rawPrivKey).toString()
    }


(): PubKey => {

        return new PubKey([
            bigInt(this.rawPubKey[0].toString()),
            bigInt(this.rawPubKey[1].toString()),
        ])
    }


() => {
        return {
            x: this.rawPubKey[0].toString(),
            y: this.rawPubKey[1].toString(),
        }
    }


() => {
        return this.rawPubKey.map((x) => x.toString())
    }


(): SnarkBigInt[] => {
        return [
            this.rawPubKey[0],
            this.rawPubKey[1],
        ]
    }


(): SnarkBigInt[] => {

        return [
            this.iv,
            ...this.data,
        ]
    }


() => {
        return {
            iv: this.iv.toString(),
            data: this.data.map((x: SnarkBigInt) => x.toString()),
        }
    }


(x: SnarkBigInt) => x.toString()


(): SnarkBigInt[] => {

        return this.asArray()
    }


(): SnarkBigInt => {

        return hash(this.asArray())
    }


(): Message => {

        return new Message(
            bigInt(this.iv.toString()),
            this.data.map((x: SnarkBigInt) => bigInt(x.toString())),
        )
    }


(x: SnarkBigInt) => bigInt(x.toString())


(): SnarkBigInt[] => {

        return [
            ...this.pubKey.asArray(),
            this.voteOptionTreeRoot,
            this.voiceCreditBalance,
            this.nonce,
        ]
    }


(): Command => {

        return new Command(
            bigInt(this.stateIndex.toString()),
            this.newPubKey.copy(),
            bigInt(this.voteOptionIndex.toString()),
            bigInt(this.newVoteWeight.toString()),
            bigInt(this.nonce.toString()),
            bigInt(this.salt.toString()),
        )
    }


(): SnarkBigInt[] => {

        return [
            this.stateIndex,
            ...this.newPubKey.asArray(),
            this.voteOptionIndex,
            this.newVoteWeight,
            this.nonce,
            this.salt,
        ]
    }


(command: Command): boolean => {

        return this.stateIndex == command.stateIndex &&
            this.newPubKey[0] == command.newPubKey[0] &&
            this.newPubKey[1] == command.newPubKey[1] &&
            this.voteOptionIndex == command.voteOptionIndex &&
            this.newVoteWeight == command.newVoteWeight &&
            this.nonce == command.nonce &&
            this.salt == command.salt
    }


(
        privKey: PrivKey,
    ): Signature => {

        return sign(privKey.rawPrivKey, hash(this.asArray()))
    }


(
        signature: Signature,
        pubKey: PubKey,
    ): boolean => {

        return verifySignature(
            hash(this.asArray()),
            signature,
            pubKey.rawPubKey,
        )
    }


(
        signature: Signature,
        sharedKey: EcdhSharedKey,
    ): Message => {

        const plaintext: Plaintext = [
            ...this.asArray(),
            signature.R8[0],
            signature.R8[1],
            signature.S,
        ]

        const ciphertext: Ciphertext = encrypt(plaintext, sharedKey)
        const message = new Message(ciphertext.iv, ciphertext.data)

        return message
    }


(
        message: Message,
        sharedKey: EcdhSharedKey,
    ) => {

        const decrypted = decrypt(message, sharedKey)

        const command = new Command(
            decrypted[0],
            new PubKey([decrypted[1], decrypted[2]]),
            decrypted[3],
            decrypted[4],
            decrypted[5],
            decrypted[6],
        )

        const signature = {
            R8: [decrypted[7], decrypted[8]],
            S: decrypted[9],
        }

        return { command, signature }
    }


class Keypair implements Keypair {
    public privKey: PrivKey
    public pubKey: PubKey

    constructor (
        privKey?: PrivKey,
    ) {
        if (privKey) {
            this.privKey = privKey
            this.pubKey = new PubKey(genPubKey(privKey.rawPrivKey))
        } else {
            const rawKeyPair = genKeypair()
            this.privKey = new PrivKey(rawKeyPair.privKey)
            this.pubKey = new PubKey(rawKeyPair.pubKey)
        }
    }

    public copy = (): Keypair => {
        return new Keypair(this.privKey.copy())
    }

    public static genEcdhSharedKey(
        privKey: PrivKey,
        pubKey: PubKey,
    ) {
        return genEcdhSharedKey(privKey.rawPrivKey, pubKey.rawPubKey)
    }

    public equals(
        keypair: Keypair,
    ): boolean {

        const equalPrivKey = this.privKey.rawPrivKey === keypair.privKey.rawPrivKey
        const equalPubKey =
            this.pubKey.rawPubKey[0] === keypair.pubKey.rawPubKey[0] &&
            this.pubKey.rawPubKey[1] === keypair.pubKey.rawPubKey[1]

        // If this assertion fails, something is very wrong and this function
        // should not return anything
        // XOR is equivalent to: (x && !y) || (!x && y )
        const x = (equalPrivKey && equalPubKey)
        const y = (!equalPrivKey && !equalPubKey)

        assert((x && !y) || (!x && y))

        return equalPrivKey
    }
}


class PrivKey {
    public rawPrivKey: RawPrivKey

    constructor (rawPrivKey: RawPrivKey) {
        this.rawPrivKey = rawPrivKey
    }

    public copy = (): PrivKey => {
        return new PrivKey(bigInt(this.rawPrivKey.toString()))
    }

    public asCircuitInputs = () => {
        return formatPrivKeyForBabyJub(this.rawPrivKey).toString()
    }
}


class PubKey {
    public rawPubKey: RawPubKey

    constructor (rawPubKey: RawPubKey) {
        assert(rawPubKey.length === 2)
        this.rawPubKey = rawPubKey
    }

    public copy = (): PubKey => {

        return new PubKey([
            bigInt(this.rawPubKey[0].toString()),
            bigInt(this.rawPubKey[1].toString()),
        ])
    }

    public asContractParam = () => {
        return {
            x: this.rawPubKey[0].toString(),
            y: this.rawPubKey[1].toString(),
        }
    }

    public asCircuitInputs = () => {
        return this.rawPubKey.map((x) => x.toString())
    }

    public asArray = (): SnarkBigInt[] => {
        return [
            this.rawPubKey[0],
            this.rawPubKey[1],
        ]
    }
}


/*
 * An encrypted command and signature.
 */
class Message {
    public iv: SnarkBigInt
    public data: SnarkBigInt[]

    constructor (
        iv: SnarkBigInt,
        data: SnarkBigInt[],
    ) {
        assert(data.length === 10)
        this.iv = iv
        this.data = data
    }

    private asArray = (): SnarkBigInt[] => {

        return [
            this.iv,
            ...this.data,
        ]
    }

    public asContractParam = () => {
        return {
            iv: this.iv.toString(),
            data: this.data.map((x: SnarkBigInt) => x.toString()),
        }
    }

    public asCircuitInputs = (): SnarkBigInt[] => {

        return this.asArray()
    }

    public hash = (): SnarkBigInt => {

        return hash(this.asArray())
    }

    public copy = (): Message => {

        return new Message(
            bigInt(this.iv.toString()),
            this.data.map((x: SnarkBigInt) => bigInt(x.toString())),
        )
    }
}


/*
 * A leaf in the state tree, which maps public keys to votes
 */
class StateLeaf implements IStateLeaf {
    public pubKey: PubKey
    public voteOptionTreeRoot: SnarkBigInt
    public voiceCreditBalance: SnarkBigInt
    public nonce: SnarkBigInt

    constructor (
        pubKey: PubKey,
        voteOptionTreeRoot: SnarkBigInt,
        voiceCreditBalance: SnarkBigInt,
        nonce: SnarkBigInt,
    ) {
        this.pubKey = pubKey
        this.voteOptionTreeRoot = voteOptionTreeRoot
        this.voiceCreditBalance = voiceCreditBalance
        // The this is the current nonce. i.e. a user who has published 0 valid
        // command should have this value at 0, and the first command should
        // have a nonce of 1
        this.nonce = nonce
    }

    public copy(): StateLeaf {
        return new StateLeaf(
            this.pubKey.copy(),
            bigInt(this.voteOptionTreeRoot.toString()),
            bigInt(this.voiceCreditBalance.toString()),
            bigInt(this.nonce.toString()),
        )
    }

    public static genBlankLeaf(
        emptyVoteOptionTreeRoot: SnarkBigInt,
    ): StateLeaf {
        return new StateLeaf(
            new PubKey([0, 0]),
            emptyVoteOptionTreeRoot,
            bigInt(0),
            bigInt(0),
        )
    }

    public static genRandomLeaf() {
        return new StateLeaf(
            new PubKey([genRandomSalt(), genRandomSalt()]),
            genRandomSalt(),
            genRandomSalt(),
            genRandomSalt(),
        )
    }

    private asArray = (): SnarkBigInt[] => {

        return [
            ...this.pubKey.asArray(),
            this.voteOptionTreeRoot,
            this.voiceCreditBalance,
            this.nonce,
        ]
    }

    public asCircuitInputs = (): SnarkBigInt[] => {

        return this.asArray()
    }

    public hash = (): SnarkBigInt => {

        return hash(this.asArray())
    }
}


/*
 * Unencrypted data whose fields include the user's public key, vote etc.
 */
class Command implements ICommand {
    public stateIndex: SnarkBigInt
    public newPubKey: PubKey
    public voteOptionIndex: SnarkBigInt
    public newVoteWeight: SnarkBigInt
    public nonce: SnarkBigInt
    public salt: SnarkBigInt

    constructor (
        stateIndex: SnarkBigInt,
        newPubKey: PubKey,
        voteOptionIndex: SnarkBigInt,
        newVoteWeight: SnarkBigInt,
        nonce: SnarkBigInt,
        salt: SnarkBigInt = genRandomSalt(),
    ) {
        this.stateIndex = stateIndex
        this.newPubKey = newPubKey
        this.voteOptionIndex = voteOptionIndex
        this.newVoteWeight = newVoteWeight
        this.nonce = nonce
        this.salt = salt
    }

    public copy = (): Command => {

        return new Command(
            bigInt(this.stateIndex.toString()),
            this.newPubKey.copy(),
            bigInt(this.voteOptionIndex.toString()),
            bigInt(this.newVoteWeight.toString()),
            bigInt(this.nonce.toString()),
            bigInt(this.salt.toString()),
        )
    }

    public asArray = (): SnarkBigInt[] => {

        return [
            this.stateIndex,
            ...this.newPubKey.asArray(),
            this.voteOptionIndex,
            this.newVoteWeight,
            this.nonce,
            this.salt,
        ]
    }

    /*
     * Check whether this command has deep equivalence to another command
     */
    public equals = (command: Command): boolean => {

        return this.stateIndex == command.stateIndex &&
            this.newPubKey[0] == command.newPubKey[0] &&
            this.newPubKey[1] == command.newPubKey[1] &&
            this.voteOptionIndex == command.voteOptionIndex &&
            this.newVoteWeight == command.newVoteWeight &&
            this.nonce == command.nonce &&
            this.salt == command.salt
    }

    /*
     * Signs this command and returns a Signature.
     */
    public sign = (
        privKey: PrivKey,
    ): Signature => {

        return sign(privKey.rawPrivKey, hash(this.asArray()))
    }

    /*
     * Returns true if the given signature is a correct signature of this
     * command and signed by the private key associated with the given public
     * key.
     */
    public verifySignature = (
        signature: Signature,
        pubKey: PubKey,
    ): boolean => {

        return verifySignature(
            hash(this.asArray()),
            signature,
            pubKey.rawPubKey,
        )
    }

    /*
     * Encrypts this command along with a signature to produce a Message.
     */
    public encrypt = (
        signature: Signature,
        sharedKey: EcdhSharedKey,
    ): Message => {

        const plaintext: Plaintext = [
            ...this.asArray(),
            signature.R8[0],
            signature.R8[1],
            signature.S,
        ]

        const ciphertext: Ciphertext = encrypt(plaintext, sharedKey)
        const message = new Message(ciphertext.iv, ciphertext.data)

        return message
    }

    /*
     * Decrypts a Message to produce a Command.
     */
    public static decrypt = (
        message: Message,
        sharedKey: EcdhSharedKey,
    ) => {

        const decrypted = decrypt(message, sharedKey)

        const command = new Command(
            decrypted[0],
            new PubKey([decrypted[1], decrypted[2]]),
            decrypted[3],
            decrypted[4],
            decrypted[5],
            decrypted[6],
        )

        const signature = {
            R8: [decrypted[7], decrypted[8]],
            S: decrypted[9],
        }

        return { command, signature }
    }
}


interface Keypair {
    privKey: RawPrivKey,
    pubKey: RawPubKey,
}


interface IStateLeaf {
    pubKey: PubKey;
    voteOptionTreeRoot: SnarkBigInt;
    voiceCreditBalance: SnarkBigInt;
    nonce: SnarkBigInt;
}


interface VoteOptionTreeLeaf {
    votes: SnarkBigInt;
}


interface ICommand {
    stateIndex: SnarkBigInt;
    newPubKey: PubKey;
    voteOptionIndex: SnarkBigInt;
    newVoteWeight: SnarkBigInt;
    nonce: SnarkBigInt;

    sign: (PrivKey) => Signature;
    encrypt: (EcdhSharedKey, Signature) => Message;
}


public static genEcdhSharedKey(
        privKey: PrivKey,
        pubKey: PubKey,
    ) {
        return genEcdhSharedKey(privKey.rawPrivKey, pubKey.rawPubKey)
    }


public equals(
        keypair: Keypair,
    ): boolean {

        const equalPrivKey = this.privKey.rawPrivKey === keypair.privKey.rawPrivKey
        const equalPubKey =
            this.pubKey.rawPubKey[0] === keypair.pubKey.rawPubKey[0] &&
            this.pubKey.rawPubKey[1] === keypair.pubKey.rawPubKey[1]

        // If this assertion fails, something is very wrong and this function
        // should not return anything
        // XOR is equivalent to: (x && !y) || (!x && y )
        const x = (equalPrivKey && equalPubKey)
        const y = (!equalPrivKey && !equalPubKey)

        assert((x && !y) || (!x && y))

        return equalPrivKey
    }


public copy(): StateLeaf {
        return new StateLeaf(
            this.pubKey.copy(),
            bigInt(this.voteOptionTreeRoot.toString()),
            bigInt(this.voiceCreditBalance.toString()),
            bigInt(this.nonce.toString()),
        )
    }


public static genBlankLeaf(
        emptyVoteOptionTreeRoot: SnarkBigInt,
    ): StateLeaf {
        return new StateLeaf(
            new PubKey([0, 0]),
            emptyVoteOptionTreeRoot,
            bigInt(0),
            bigInt(0),
        )
    }


public static genRandomLeaf() {
        return new StateLeaf(
            new PubKey([genRandomSalt(), genRandomSalt()]),
            genRandomSalt(),
            genRandomSalt(),
            genRandomSalt(),
        )
    }


(data) => data.json()


(data) => {
    const radarPlot = new Radar('container', {
      data,
      xField: 'item',
      yField: 'score',
      meta: {
        score: {
          alias: '分数',
        },
      },
      xAxis: {
        line: null,
        tickLine: null,
        grid: {
          line: {
            style: {
              lineDash: null,
            },
          },
        },
      },
      yAxis: {
        line: null,
        tickLine: null,
        grid: {
          line: {
            type: 'line',
            style: {
              lineDash: null,
            },
          },
          alternateColor: 'rgba(0, 0, 0, 0.04)',
        },
      },
      point: {},
    });
    radarPlot.render();
  }


(error: errors.SpreeSDKError): string => {
  let prettyOutput = `Name: ${error.name}\nMessage: ${error.message}`

  if (error instanceof errors.BasicSpreeError) {
    prettyOutput += `\nSpree summary: ${error.summary}`

    if (error instanceof errors.ExpandedSpreeError) {
      prettyOutput += `\nSpree validation errors:\n${JSON.stringify(
        error.errors,
        null,
        2
      )}`
    }
  }

  return prettyOutput
}


// 获取时间的数字类型
export function getTimeNum (date) {
  const dateNum = !date ? 0 : new Date(date).getTime()
  return dateNum
}


// 比对时间
export function compareDate (a, b) {
  const aDateNum = getTimeNum(a.frontmatter.date)
  const bDateNum = getTimeNum(b.frontmatter.date)
  if (aDateNum === 0 || bDateNum === 0) return 0
  return bDateNum - aDateNum
}


(option: PageOptions) => {
      if ((option as ClassificationPageOptions).type === 'frontmatter') {
        const {
          frontmatterKey: key,
          pagination,
          layout,
        } = option as ClassificationPageOptions

        this.frontmatterKeys.push(key)

        this.classificationData[key] = {
          pagination: pagination || 10, // 分页默认为 10
          extendedPages: [],
          items: {},
          layout,
        }
      }
    }


(page: Page) => {
      page.path = convertToPinyin(decodeURIComponent(page.path))
      return page
    }


(page: Page) => {
        const publishFlag = !(
          page?.frontmatter?.home === true
            || page?.frontmatter?.publish === false
            || page?.title === '')

        if (autoSetCategory && publishFlag) {
          this.setCategory(page)
        }

        return publishFlag
      }


(prev, next) => {
        const prevSticky = prev.frontmatter.sticky as number
        const nextSticky = next.frontmatter.sticky as number

        if (prevSticky && nextSticky) {
          return prevSticky == nextSticky ? compareDate(prev, next) : (prevSticky - nextSticky)
        } else if (prevSticky && !nextSticky) {
          return -1
        } else if (!prevSticky && nextSticky) {
          return 1
        }

        return compareDate(prev, next)
      }


(page: Page) => {
      if (
        page?.frontmatter?.home === true ||
        page?.frontmatter?.publish === false ||
        page?.title === ''
      ) {
        return
      }

      const classificationKeys = Object.keys(page.frontmatter).filter(
        (key: string) => {
          return this.frontmatterKeys.includes(key)
        }
      )

      classificationKeys.forEach((key: FrontmatterKey) => {
        const values = page.frontmatter[key] as string[]
        if (isEmptyPlainObject(this.classificationData[key].items)) {
          this.classificationData[key].items = values.reduce(
            (total, current) => {
              total[convertToPinyin(current)] = {
                pages: [page],
                length: 1,
                label: current
              }
              return total
            },
            {}
          )
        } else {
          values.forEach((value: ItemKey) => {
            if (!this.classificationData[key].items[convertToPinyin(value)]) {
              this.classificationData[key].items[convertToPinyin(value)] = {
                pages: [page],
                length: 1,
                label: value
              }
            } else {
              const { pages: p, length, } =
                this.classificationData[key].items[convertToPinyin(value)]
              this.classificationData[key].items[convertToPinyin(value)] = {
                length: length + 1,
                pages: [...p, page],
                label: value
              }
            }
          })
        }
      })
    }


(key: string) => {
          return this.frontmatterKeys.includes(key)
        }


(key: FrontmatterKey) => {
        const values = page.frontmatter[key] as string[]
        if (isEmptyPlainObject(this.classificationData[key].items)) {
          this.classificationData[key].items = values.reduce(
            (total, current) => {
              total[convertToPinyin(current)] = {
                pages: [page],
                length: 1,
                label: current
              }
              return total
            },
            {}
          )
        } else {
          values.forEach((value: ItemKey) => {
            if (!this.classificationData[key].items[convertToPinyin(value)]) {
              this.classificationData[key].items[convertToPinyin(value)] = {
                pages: [page],
                length: 1,
                label: value
              }
            } else {
              const { pages: p, length, } =
                this.classificationData[key].items[convertToPinyin(value)]
              this.classificationData[key].items[convertToPinyin(value)] = {
                length: length + 1,
                pages: [...p, page],
                label: value
              }
            }
          })
        }
      }


(total, current) => {
              total[convertToPinyin(current)] = {
                pages: [page],
                length: 1,
                label: current
              }
              return total
            }


(value: ItemKey) => {
            if (!this.classificationData[key].items[convertToPinyin(value)]) {
              this.classificationData[key].items[convertToPinyin(value)] = {
                pages: [page],
                length: 1,
                label: value
              }
            } else {
              const { pages: p, length, } =
                this.classificationData[key].items[convertToPinyin(value)]
              this.classificationData[key].items[convertToPinyin(value)] = {
                length: length + 1,
                pages: [...p, page],
                label: value
              }
            }
          }


(key: string) => {
      const { items, layout, pagination } = this.classificationData[key]
      const valuesOfKey = Object.keys(items)

      this.classificationData[key].extendedPages = valuesOfKey.reduce(
        (total: Promise<Page>[], value: string) => {
          const num = items[convertToPinyin(value)].length
          const pageSize = Math.ceil(num / pagination)

          const pages = Array.from({ length: pageSize }).map((item, index) => {
            return createPage(this.app, {
              path: `/${key}/${convertToPinyin(value)}/${index + 1}/`,
              frontmatter: { layout },
            })
          })

          return [...total, ...pages]
        },
        []
      )
    }


(total: Promise<Page>[], value: string) => {
          const num = items[convertToPinyin(value)].length
          const pageSize = Math.ceil(num / pagination)

          const pages = Array.from({ length: pageSize }).map((item, index) => {
            return createPage(this.app, {
              path: `/${key}/${convertToPinyin(value)}/${index + 1}/`,
              frontmatter: { layout },
            })
          })

          return [...total, ...pages]
        }


(item, index) => {
            return createPage(this.app, {
              path: `/${key}/${convertToPinyin(value)}/${index + 1}/`,
              frontmatter: { layout },
            })
          }


(item, index) => {
      return createPage(this.app, {
        path: `/posts/${index + 1}/`,
        frontmatter: { layout: 'Post' },
      })
    }


(total: Promise<Page>[], option) => {
      const classificationPages = this.resolvePageOptions(option)
      return [...total, ...classificationPages]
    }


(key: string) => {
      const { items, pagination } = this.classificationData[key]
      const valuesOfKey = Object.keys(items)

      valuesOfKey.forEach((value: string) => {
        const { length, pages } = items[value]
        const pageSize = Math.ceil(length / pagination)

        const paginationDataOfValue = Array.from({
          length: pageSize,
        }).reduce(
          (
            total: Record<string, ClassificationPaginationPost>,
            current,
            index
          ) => {
            const currentPage = index + 1

            total[`/${key}/${convertToPinyin(value)}/${currentPage}/`] = {
              pageSize: pagination,
              total: pages.length,
              currentPage,
              currentClassificationKey: key,
              currentClassificationValue: value,
              pages:
                index < pageSize - 1
                  ? pages.slice(pagination * index, pagination)
                  : pages.slice(pagination * index)
            }
            return total
          },
          {}
        )

        data = { ...data, ...paginationDataOfValue }
      })
    }


(value: string) => {
        const { length, pages } = items[value]
        const pageSize = Math.ceil(length / pagination)

        const paginationDataOfValue = Array.from({
          length: pageSize,
        }).reduce(
          (
            total: Record<string, ClassificationPaginationPost>,
            current,
            index
          ) => {
            const currentPage = index + 1

            total[`/${key}/${convertToPinyin(value)}/${currentPage}/`] = {
              pageSize: pagination,
              total: pages.length,
              currentPage,
              currentClassificationKey: key,
              currentClassificationValue: value,
              pages:
                index < pageSize - 1
                  ? pages.slice(pagination * index, pagination)
                  : pages.slice(pagination * index)
            }
            return total
          },
          {}
        )

        data = { ...data, ...paginationDataOfValue }
      }


(
            total: Record<string, ClassificationPaginationPost>,
            current,
            index
          ) => {
            const currentPage = index + 1

            total[`/${key}/${convertToPinyin(value)}/${currentPage}/`] = {
              pageSize: pagination,
              total: pages.length,
              currentPage,
              currentClassificationKey: key,
              currentClassificationValue: value,
              pages:
                index < pageSize - 1
                  ? pages.slice(pagination * index, pagination)
                  : pages.slice(pagination * index)
            }
            return total
          }


// 初始化
init(): void {
    this.options.forEach((option: PageOptions) => {
      if ((option as ClassificationPageOptions).type === 'frontmatter') {
        const {
          frontmatterKey: key,
          pagination,
          layout,
        } = option as ClassificationPageOptions

        this.frontmatterKeys.push(key)

        this.classificationData[key] = {
          pagination: pagination || 10, // 分页默认为 10
          extendedPages: [],
          items: {},
          layout,
        }
      }
    })
  }


// 解析 key-value 对应的数量
resolveKeyValue(): void {
    this.app.pages = this.app.pages.map((page: Page) => {
      page.path = convertToPinyin(decodeURIComponent(page.path))
      return page
    })

    const { autoSetCategory } = this.app.options.themeConfig
    const publishPosts = this.app.pages
      .filter((page: Page) => {
        const publishFlag = !(
          page?.frontmatter?.home === true
            || page?.frontmatter?.publish === false
            || page?.title === '')

        if (autoSetCategory && publishFlag) {
          this.setCategory(page)
        }

        return publishFlag
      })
      .sort((prev, next) => {
        const prevSticky = prev.frontmatter.sticky as number
        const nextSticky = next.frontmatter.sticky as number

        if (prevSticky && nextSticky) {
          return prevSticky == nextSticky ? compareDate(prev, next) : (prevSticky - nextSticky)
        } else if (prevSticky && !nextSticky) {
          return -1
        } else if (!prevSticky && nextSticky) {
          return 1
        }

        return compareDate(prev, next)
      })

    this.publishPosts = publishPosts

    publishPosts.forEach((page: Page) => {
      if (
        page?.frontmatter?.home === true ||
        page?.frontmatter?.publish === false ||
        page?.title === ''
      ) {
        return
      }

      const classificationKeys = Object.keys(page.frontmatter).filter(
        (key: string) => {
          return this.frontmatterKeys.includes(key)
        }
      )

      classificationKeys.forEach((key: FrontmatterKey) => {
        const values = page.frontmatter[key] as string[]
        if (isEmptyPlainObject(this.classificationData[key].items)) {
          this.classificationData[key].items = values.reduce(
            (total, current) => {
              total[convertToPinyin(current)] = {
                pages: [page],
                length: 1,
                label: current
              }
              return total
            },
            {}
          )
        } else {
          values.forEach((value: ItemKey) => {
            if (!this.classificationData[key].items[convertToPinyin(value)]) {
              this.classificationData[key].items[convertToPinyin(value)] = {
                pages: [page],
                length: 1,
                label: value
              }
            } else {
              const { pages: p, length, } =
                this.classificationData[key].items[convertToPinyin(value)]
              this.classificationData[key].items[convertToPinyin(value)] = {
                length: length + 1,
                pages: [...p, page],
                label: value
              }
            }
          })
        }
      })
    })

    this.resolvePages()
  }


// 根据数量转化成 page 信息
resolvePages(): void {
    this.frontmatterKeys.forEach((key: string) => {
      const { items, layout, pagination } = this.classificationData[key]
      const valuesOfKey = Object.keys(items)

      this.classificationData[key].extendedPages = valuesOfKey.reduce(
        (total: Promise<Page>[], value: string) => {
          const num = items[convertToPinyin(value)].length
          const pageSize = Math.ceil(num / pagination)

          const pages = Array.from({ length: pageSize }).map((item, index) => {
            return createPage(this.app, {
              path: `/${key}/${convertToPinyin(value)}/${index + 1}/`,
              frontmatter: { layout },
            })
          })

          return [...total, ...pages]
        },
        []
      )
    })
  }


// 解析 page 配置
private resolvePageOptions(option: PageOptions): Promise<Page>[] {
    if ((option as ClassificationPageOptions).type === 'frontmatter') {
      return this.classificationData[
        (option as ClassificationPageOptions).frontmatterKey
      ].extendedPages
    } else {
      const { path, layout } = option
      return [
        createPage(this.app, {
          frontmatter: { layout },
          path: convertToPinyin(path),
        }),
      ]
    }
  }


// 设置类别
private setCategory(page) {
    const blogCategray = ((page.filePath || '') as string).match(/.+\/blogs\/(.+)\/.+\.md$/)
    if (blogCategray) page.frontmatter.categories = [blogCategray[1]]

    const docCategray = ((page.filePath || '') as string).match(/.+\/docs\/(.+)\/.+\.md$/)
    if (docCategray) page.frontmatter.categories = [docCategray[1]]
  }


getPublishPostsPage(): Array<Promise<Page>> {
    const pageSize = Math.ceil(this.publishPosts.length / 10)

    const pages = Array.from({ length: pageSize }).map((item, index) => {
      return createPage(this.app, {
        path: `/posts/${index + 1}/`,
        frontmatter: { layout: 'Post' },
      })
    })

    return pages
  }


function sanitizeString(str) {
  if (str.includes('*')) {
    throw new VariableDynamicImportError('A dynamic import cannot contain * characters.');
  }
  return str;
}


function templateLiteralToGlob(node) {
  let glob = '';

  for (let i = 0; i < node.quasis.length; i += 1) {
    glob += sanitizeString(node.quasis[i].value.raw);
    if (node.expressions[i]) { // quasis 永远比 expressions 长一位
      glob += expressionToGlob(node.expressions[i]);
    }
  }

  return glob;
}


function callExpressionToGlob(node) {
  const { callee } = node;
  if (
    callee.type === 'MemberExpression' &&
    callee.property.type === 'Identifier' &&
    callee.property.name === 'concat'
  ) {
    return `${expressionToGlob(callee.object)}${node.arguments.map(expressionToGlob).join('')}`;
  }
  return '*';
}


function binaryExpressionToGlob(node) {
  if (node.operator !== '+') {
    throw new VariableDynamicImportError(`${node.operator} operator is not supported.`);
  }

  return `${expressionToGlob(node.left)}${expressionToGlob(node.right)}`;
}


function expressionToGlob(node) {
  switch (node.type) {
    case 'TemplateLiteral':
      // import(`@/pages/${path}`)
      return templateLiteralToGlob(node);
    case 'CallExpression':
      // import('@/pages/'.concat(path))
      return callExpressionToGlob(node);
    case 'BinaryExpression':
      // import('@/pages/' + path)
      return binaryExpressionToGlob(node);
    case 'Literal': {
      // import('@/pages/path')
      return sanitizeString(node.value);
    }
    default:
      return '**/*';
  }
}


function dynamicImportToGlob(node, sourceString) {
  let glob = expressionToGlob(node);
  if (!glob.includes('*') || glob.startsWith('data:')) {
    return null;
  }
  glob = glob.replace(/\*\*/g, '*');
  glob = DynamicImportVars.aliasReplace(glob);

  if (glob.startsWith('*')) {
    throw new VariableDynamicImportError(
      `invalid import "${sourceString}". It cannot be statically analyzed. Variable dynamic imports must start with ./ and be limited to a specific directory. ${example}`
    );
  }

  if (glob.startsWith('/')) {
    throw new VariableDynamicImportError(
      `invalid import "${sourceString}". Variable absolute imports are not supported, imports must start with ./ in the static part of the import. ${example}`
    );
  }

  if (!glob.startsWith('./') && !glob.startsWith('../')) {
    throw new VariableDynamicImportError(
      `invalid import "${sourceString}". Variable bare imports are not supported, imports must start with ./ in the static part of the import. ${example}`
    );
  }

  // Disallow ./*.ext
  const ownDirectoryStarExtension = /^\.\/\*\.[\w]+$/;
  if (ownDirectoryStarExtension.test(glob)) {
    throw new VariableDynamicImportError(
      `${`invalid import "${sourceString}". Variable imports cannot import their own directory, ` +
      'place imports in a separate directory or make the import filename more specific. '
      }${example}`
    );
  }

  // if (path.extname(glob) === '') {
  //   throw new VariableDynamicImportError(
  //     `invalid import "${sourceString}". A file extension must be included in the static part of the import. ${example}`
  //   );
  // }

  return glob;
}


export class DynamicImportVars {
  static aliasContext: AliasContext
  static _id: string
  static _alias: ImporteeGlob['alias']

  static aliasReplace(globImportee: string): string {
    const replaced = this.aliasContext.replaceImportee(globImportee, this._id)
    this._alias = replaced || undefined
    return replaced ? replaced.replacedImportee : globImportee
  }

  constructor(
    aliasContext: AliasContext
  ) {
    DynamicImportVars.aliasContext = aliasContext
  }

  public dynamicImportToGlob(
    node: AcornNode,
    sourceString: string,
    id: string,
  ): ImporteeGlob {
    DynamicImportVars._id = id

    const glob = dynamicImportToGlob(node, sourceString)
    const result: ImporteeGlob = {
      glob,
      alias: DynamicImportVars._alias,
    }

    DynamicImportVars._id = undefined
    DynamicImportVars._alias = undefined

    return result
  }
}


// The following part is the `@rollup/plugin-dynamic-import-vars` source code
// https://github.com/rollup/plugins/blob/master/packages/dynamic-import-vars/src/dynamic-import-to-glob.js
class VariableDynamicImportError extends Error { }


export interface ImporteeGlob {
  glob: string | null
  /**
   * with alias glob
   */
  alias?: AliasReplaced
}


static aliasReplace(globImportee: string): string {
    const replaced = this.aliasContext.replaceImportee(globImportee, this._id)
    this._alias = replaced || undefined
    return replaced ? replaced.replacedImportee : globImportee
  }


public dynamicImportToGlob(
    node: AcornNode,
    sourceString: string,
    id: string,
  ): ImporteeGlob {
    DynamicImportVars._id = id

    const glob = dynamicImportToGlob(node, sourceString)
    const result: ImporteeGlob = {
      glob,
      alias: DynamicImportVars._alias,
    }

    DynamicImportVars._id = undefined
    DynamicImportVars._alias = undefined

    return result
  }


(token: TagToken) => {
        if (token.name === 'endraw') stream.stop()
        else this.tokens.push(token)
      }


() => {
        throw new Error(`tag ${tagToken.raw} not closed`)
      }


(token: Token) => token.raw


(type) => [StationModel]


(type) => StationModel


(station) => station.city


@ObjectType()
@Entity()
export class CityModel {
  @Field()
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Field()
  @Column({ length: 50, nullable: false })
  name: string;

  @Field()
  @Column('varchar', { length: 15 })
  phone: string;

  @Field((type) => [StationModel], { nullable: true })
  @OneToMany((type) => StationModel, (station) => station.city)
  stations: StationModel[];
}


interface MyApp {
        "offset": number;
    }


interface MyItem {
        "index": number;
    }


interface MyList {
        "offset": number;
    }


interface HTMLMyAppElement extends Components.MyApp, HTMLStencilElement {
    }


interface HTMLMyItemElement extends Components.MyItem, HTMLStencilElement {
    }


interface HTMLMyListElement extends Components.MyList, HTMLStencilElement {
    }


interface HTMLElementTagNameMap {
        "my-app": HTMLMyAppElement;
        "my-item": HTMLMyItemElement;
        "my-list": HTMLMyListElement;
    }


interface MyApp {
        "offset"?: number;
    }


interface MyItem {
        "index"?: number;
    }


interface MyList {
        "offset"?: number;
    }


interface IntrinsicElements {
        "my-app": MyApp;
        "my-item": MyItem;
        "my-list": MyList;
    }


interface IntrinsicElements {
            "my-app": LocalJSX.MyApp & JSXBase.HTMLAttributes<HTMLMyAppElement>;
            "my-item": LocalJSX.MyItem & JSXBase.HTMLAttributes<HTMLMyItemElement>;
            "my-list": LocalJSX.MyList & JSXBase.HTMLAttributes<HTMLMyListElement>;
        }


@Injectable()

export class ContratoService {
    apiUrl: string;
    constructor(private http: HttpClient) { }

    buscarDestino(): Observable<Contrato[]> {
        return this.http.get<Contrato[]>(API_URL);
    }

    crearOActualizarDestino(contrato: Contrato): Observable<Contrato[]> {
        return this.http.post<Contrato[]>(API_URL_CREATE, contrato);
    }
    delateDestino(contrato: Contrato): Observable<Contrato[]> {
        return this.http.post<Contrato[]>(API_URL_DELATE, contrato);
    }
}


buscarDestino(): Observable<Contrato[]> {
        return this.http.get<Contrato[]>(API_URL);
    }


crearOActualizarDestino(contrato: Contrato): Observable<Contrato[]> {
        return this.http.post<Contrato[]>(API_URL_CREATE, contrato);
    }


delateDestino(contrato: Contrato): Observable<Contrato[]> {
        return this.http.post<Contrato[]>(API_URL_DELATE, contrato);
    }


export function Header({ selectedGenre }: GenreResponseProps) {
  return (

        Categoria: {selectedGenre.title}

  );
}


interface GenreResponseProps {
  selectedGenre: {
    title: string;
  };
}


//melhorar validacoes nos metodos
export class RegrasController {

    async index(req: Request, res: Response) {
        if (new JsonWebToken().verificaToken(req.headers.authorization)) {
            try {
                const regrasservice = new RegrasService();
                const results = await regrasservice.index()
                return res.json(results)
            } catch (err) {
                return res.status(400).json("Ocorreu algum erro!")
            }
        } else {
            return res.status(401).json("Você não tem permissão para esta rota !")
        }

    }

    async create(req: Request, res: Response, next: NextFunction) {
        if (new JsonWebToken().verificaToken(req.headers.authorization)) {
            const body = req.body as Regras
            const regrasservice = new RegrasService()
            body.arquivo = req.file.filename
            const respostas = await regrasservice.create(body)
            return res.status(respostas.status).json(respostas.resposta)
        } else {
            return res.status(401).json("Você não tem permissão para esta rota !")
        }

    }

    async delete(req: Request, res: Response, next: NextFunction) {
        if (new JsonWebToken().verificaToken(req.headers.authorization)) {
            const id = req.params.id
            const regrasservice = new RegrasService();
            var respostas = await regrasservice.delete(id)
            return res.status(respostas.status).json(respostas.resposta)
        } else {
            return res.status(401).json("Você não tem permissão para esta rota !")
        }

    }

}


async index(req: Request, res: Response) {
        if (new JsonWebToken().verificaToken(req.headers.authorization)) {
            try {
                const regrasservice = new RegrasService();
                const results = await regrasservice.index()
                return res.json(results)
            } catch (err) {
                return res.status(400).json("Ocorreu algum erro!")
            }
        } else {
            return res.status(401).json("Você não tem permissão para esta rota !")
        }

    }


async create(req: Request, res: Response, next: NextFunction) {
        if (new JsonWebToken().verificaToken(req.headers.authorization)) {
            const body = req.body as Regras
            const regrasservice = new RegrasService()
            body.arquivo = req.file.filename
            const respostas = await regrasservice.create(body)
            return res.status(respostas.status).json(respostas.resposta)
        } else {
            return res.status(401).json("Você não tem permissão para esta rota !")
        }

    }


async delete(req: Request, res: Response, next: NextFunction) {
        if (new JsonWebToken().verificaToken(req.headers.authorization)) {
            const id = req.params.id
            const regrasservice = new RegrasService();
            var respostas = await regrasservice.delete(id)
            return res.status(respostas.status).json(respostas.resposta)
        } else {
            return res.status(401).json("Você não tem permissão para esta rota !")
        }

    }


export function getCurveSelectionParamsWithInterval(q: QueryParamsWithInterval): string {
  switch (q.curveSelection.tag) {
    case CurveSelectionType.IdSelection:
      return "id=" + q.curveSelection.val.join(",");
    case CurveSelectionType.FilterSelection:
      return "filterId=" + q.curveSelection.val.toString();
  }
}


export function getCurveSelectionParamsWithRange(q: QueryParamsWithRange): string {
  switch (q.curveSelection.tag) {
    case CurveSelectionType.IdSelection:
      return "id=" + q.curveSelection.val.join(",");
    case CurveSelectionType.FilterSelection:
      return "filterId=" + q.curveSelection.val.toString();
  }
}


export function addTimeTransformQueryParam(q: { tr?: number }): string {
  return q.tr ? "tr=" + q.tr.toString() : "";
}


export enum LastOfType {
  Days,
  Months
}


export enum VersionSelectionType {
  LastN,
  MUV,
  LastOf,
  Version,
  ForMostRecent
}


export enum CurveSelectionType {
  IdSelection,
  FilterSelection
}


export enum ExtractionRangeType {
  DateRange,
  Period,
  PeriodRange,
  RelativeInterval
}


export enum RelativeIntervalType {
  RollingWeek,
  RollingMonth,
  RollingQuarter,
  RollingYear,
  WeekToDate,
  MonthToDate,
  QuarterToDate,
  YearToDate
}


export enum FillerKindType {
  Null="Null",
  NoFill="NoFill",
  LatestValidValue="LatestValidValue",
  CustomValue="CustomValue"
}


export type QueryParamsWithInterval = {
  extractionRange: ExtractionRange;
  curveSelection: CurveSelection;
  tz?: string;
};


export type QueryParamsWithRange = {
  extractionRange: ExtractionRangeWithoutInterval;
  curveSelection: CurveSelection;
  tz?: string;
};


export type ActualQueryParams = QueryParamsWithInterval & {
  granularity: Granularity;
  tr?: number;
  fill?: CustomFill<number>
};


export type AuctionQueryParams = QueryParamsWithRange


export type VersionedQueryParams = QueryParamsWithInterval & {
  granularity: Granularity;
  tr?: number;
  fill?: CustomFill<number>
  versionSelection: VersionSelection;
};


type MarketAssessmentValue = {
  settlement?: number;
  open?: number;
  close?: number;
  high?: number;
  low?: number;
  volumePaid?: number;
  volueGiven?: number;
  volume?: number;
};


export type MasQueryParams = QueryParamsWithInterval & {
  fill?: CustomFill<MarketAssessmentValue>;
  products: string[];
};


export type BidAskValue = {
  bestBidPrice?: number;
  bestAskPrice?: number;
  bestBidQuantity?: number;
  bestAskQuantity?: number;
  lastPrice?: number;
  lastQuantity?: number;
}


export type BidAskQueryParams = QueryParamsWithInterval & {
  fill?: CustomFill<BidAskValue>;
  products: string[];
};


export type VersionSelection = LastN | MUV | LastOf | Version | ForMostRecent;


export type LastN = { tag: VersionSelectionType.LastN; val: number };


export type MUV = { tag: VersionSelectionType.MUV };


export type LastOf = {
  tag: VersionSelectionType.LastOf;
  lastOfType: LastOfType;
  extraction: LastOfExtractionType;
};


export type ForMostRecent = {
  tag: VersionSelectionType.ForMostRecent;
  extraction: MostRecentExtractionType;
};


export type LastOfExtractionType = DateRange | Period | PeriodRange;


export type MostRecentExtractionType = DateRange | Period | PeriodRange;


export type Version = { tag: VersionSelectionType.Version; val: Date };


export type CurveSelection = IdSelection | FilterSelection;


export type IdSelection = {
  tag: CurveSelectionType.IdSelection;
  val: number[];
};


export type FilterSelection = {
  tag: CurveSelectionType.FilterSelection;
  val: number;
};


export type ExtractionRange =
  | DateRange
  | Period
  | PeriodRange
  | RelativeInterval;


export type ExtractionRangeWithoutInterval =
  | DateRange
  | Period
  | PeriodRange;


export type DateRange = {
  tag: ExtractionRangeType.DateRange;
  start: Date;
  end: Date;
};


export type Period = {
  tag: ExtractionRangeType.Period;
  Period: string;
};


export type PeriodRange = {
  tag: ExtractionRangeType.PeriodRange;
  PeriodFrom: string;
  PeriodTo: string;
};


export type RelativeInterval = {
  tag: ExtractionRangeType.RelativeInterval;
  Interval: RelativeIntervalType;
};


type CustomFill<a> =
  | {
      fillerType: FillerKindType.Null;
    }
  | {
      fillerType: FillerKindType.NoFill;
    }
  | {
      fillerType: FillerKindType.LatestValidValue;
      fillerPeriod: string;
    }
  | {
      fillerType: FillerKindType.CustomValue;
      fillerValue: a;
    };


@ApiUseTags('Skins')
@Controller('skins')
export class SkinsController {
    constructor(private readonly skinsService: SkinsService) { }

    @Post()
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    createOne(@Body() newSkin: SkinsCreateDto): Promise<ISkin> {
        return this.skinsService.createOne(newSkin);
    }

    @Get()
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitQuery({ name: 'limit', type: Number, required: false })
    @ApiImplicitQuery({ name: 'skip', type: Number, required: false })
    @ApiImplicitQuery({ name: 'sortBy', description: 'entity fields to sortBy', required: false })
    @ApiImplicitQuery({ name: 'where', description: 'entity fields to search by', required: false })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    async getByFilter(@Query() query: SkinsFindManyDto, @Req() req: Request): Promise<ISkin[]> {
        const xTotalCountNicknames = await this.skinsService.countAll();
        req.res.header('X-Total-Count', xTotalCountNicknames.toString());

        if (query.quickSearch) {
            return this.skinsService.findByPattern(query);
        }

        return this.skinsService.findAll(query);
    }

    @Get('/subscription')
    @ApiBearerAuth()
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    getSkinsForSubscription(): Promise<ISkin[]> {
        return this.skinsService.getSkinsForSubscription();
    }

    @Get('/:skinId')
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitParam({ name: 'skinId', type: String })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    getById(@Param() { skinId }: SkinsParamDto): Promise<ISkin> {
        return this.skinsService.findById(skinId);
    }

    @Put('/:skinId')
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitParam({ name: 'skinId', type: String })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    updateOne(@Param() { skinId }: SkinsParamDto, @Body() updatedNickname: SkinsUpdateDto): Promise<ISkin> {
        return this.skinsService.updateOne(skinId, updatedNickname);
    }

    @Delete('/:skinId')
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitParam({ name: 'skinId', type: String })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    deleteOne(@Param() { skinId }: SkinsParamDto): Promise<ISkin> {
        return this.skinsService.deleteById(skinId);
    }
}


@Post()
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    createOne(@Body() newSkin: SkinsCreateDto): Promise<ISkin> {
        return this.skinsService.createOne(newSkin);
    }


@Get()
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitQuery({ name: 'limit', type: Number, required: false })
    @ApiImplicitQuery({ name: 'skip', type: Number, required: false })
    @ApiImplicitQuery({ name: 'sortBy', description: 'entity fields to sortBy', required: false })
    @ApiImplicitQuery({ name: 'where', description: 'entity fields to search by', required: false })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    async getByFilter(@Query() query: SkinsFindManyDto, @Req() req: Request): Promise<ISkin[]> {
        const xTotalCountNicknames = await this.skinsService.countAll();
        req.res.header('X-Total-Count', xTotalCountNicknames.toString());

        if (query.quickSearch) {
            return this.skinsService.findByPattern(query);
        }

        return this.skinsService.findAll(query);
    }


@Get('/subscription')
    @ApiBearerAuth()
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    getSkinsForSubscription(): Promise<ISkin[]> {
        return this.skinsService.getSkinsForSubscription();
    }


@Get('/:skinId')
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitParam({ name: 'skinId', type: String })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    getById(@Param() { skinId }: SkinsParamDto): Promise<ISkin> {
        return this.skinsService.findById(skinId);
    }


@Put('/:skinId')
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitParam({ name: 'skinId', type: String })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    updateOne(@Param() { skinId }: SkinsParamDto, @Body() updatedNickname: SkinsUpdateDto): Promise<ISkin> {
        return this.skinsService.updateOne(skinId, updatedNickname);
    }


@Delete('/:skinId')
    @ApiBearerAuth()
    @Roles(['USER_ADMIN'])
    @UseGuards(AuthGuard('jwt'), RolesGuard)
    @UsePipes(new ValidationPipe())
    @ApiImplicitParam({ name: 'skinId', type: String })
    @ApiResponse({ status: 201, description: `${ENTITY} has been successfully created.` })
    @ApiResponse({ status: 404, description: `${ENTITY} not found.` })
    @ApiResponse({ status: 400, description: 'Incoming data invalid.' })
    deleteOne(@Param() { skinId }: SkinsParamDto): Promise<ISkin> {
        return this.skinsService.deleteById(skinId);
    }


<T extends any>(value: T): T | undefined => {
  const ref = useRef<T>();
  useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}


() => {
    ref.current = value;
  }


@NgModule({
  declarations: [
    AppComponent,
     HomeComponent,
   StaffComponent,
    MeetingComponent,
    NewsComponent,
    ContactComponent,
    SignInComponent,
    NavBarComponent,
    SignUpComponent,
    ForgotPasswordComponent,
    VerifyEmailComponent,
    DashboardComponent,
    FooterComponent,
    UpdateDashboardComponent,
    SidebarDashboardComponent,
    DashboardAdminComponent,
    SidebarDashadminComponent,
    UserListComponent,
    AppointmentsComponent,
    UpdateDashboardAdminComponent,
    DisablecontrolDirective,
    EditAppointmentsComponent,
    LoadingComponent,
    NewsListComponent,
    NewsEditComponent,
    NewsAddComponent,
    MeetingDoneComponent
  ],
  imports: [
    BrowserModule,
    FontAwesomeModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebaseConfig, 'polisoft'),
    AngularFirestoreModule,
    AngularFireAuthModule,
    AngularFireStorageModule,
    AngularFireDatabaseModule, // <---  Database
    HttpClientModule,
    DlDateTimeDateModule,  // <--- Calendar
    DlDateTimePickerModule,
    FormsModule,
    ReactiveFormsModule,
    BrowserAnimationsModule,
    ModalModule
  ],
  providers: [AuthService,UserService, UploadService, AppointmentService,  NewsService],
  bootstrap: [AppComponent]
})
export class AppModule { }


({
  color = 'transparent',
  onClick,
  contrastColor = '#ffffff'
}: Props) => {
  const handleCircleClick = () => onClick();

  const cursorClasses = className({
    cursor: true,
    black: contrastColor !== '#ffffff'
  });

  return (

        <div className='bg-color' style={{ backgroundColor: color }}


() => onClick()


type Props = {
  color: string;
  onClick: () => void;
  contrastColor?: string;
};


function ActiveLink({ children, href }: any) {
  const router = useRouter();

  const handleClick = (e: any) => {
    e.preventDefault();
    router.push(href);
  };

  const classActive = () => {
    return router.asPath === href ? 'link-secondary' : 'link-dark';
  };

  return (
    <a
      href={href}
      onClick={handleClick}
      className={'text-base font-medium text-gray-500 hover:text-gray-900 '.concat(classActive())}
    >
      {children}
    </a>
  );
}


(e: any) => {
    e.preventDefault();
    router.push(href);
  }


() => {
    return router.asPath === href ? 'link-secondary' : 'link-dark';
  }


/**
     * This is a simple entity for use during debugging. It can mark a cell in
     * the maze by rendering its bounds.
     */
export class Marker extends Entity
    {
        /**
         * Construct a new marker entity that will render on the stage provided.
         *
         * This needs to know the size of the cells in the grid so that it knows
         * how to render itself; this means an instance cannot be created until
         * all of the preloads are finished and we know the cell size.
         *
         * @param {Stage}  stage    the stage that we use to render ourselves
         * @param {number} cellSize the size of the cells (in pixels)
         */
        constructor (stage : Stage, cellSize : number)
        {
            // Invoke the super; note that this does not set a position because
            // we're rendered wherever we are needed. We do set our dimensions
            // however.
            super ("marker", stage, 0, 0, cellSize, cellSize, 1, {}, {}, 'white');
        }
    }


export function OptionsControl(config: OptionsBasicConfig) {
  return function <T extends React.ComponentType<OptionsControlProps>>(
    component: T
  ): T {
    const renderer = registerOptionsControl({
      ...config,
      component: component
    });
    return renderer.component as any;
  };
}


export function highlight(
  text: string,
  input?: string,
  hlClassName: string = 'is-matched'
) {
  if (!input) {
    return text;
  }

  text = String(text);
  const reg = new RegExp(
    input.replace(/([\$\^\*\+\-\?\.\(\)\|\[\]\\])/g, '\\$1'),
    'i'
  );
  if (!reg.test(text)) {
    return text;
  }

  const parts = text.split(reg);
  const dom: Array<any> = [];

  parts.forEach((text: string, index) => {
    text && dom.push({text});
    dom.push(
      <span className={hlClassName} key={`${index}-hl`}>
        {input}

    );
  }


() => JSON.stringify([formItem.loading, formItem.filteredOptions])


() => this.forceUpdate()


(selectedOption: Option) =>
                  selectedOption[valueField || 'value']


() => this.normalizeValue()


val => typeof val === 'string' || typeof val === 'number'


(selectedOption: Option) => selectedOption[valueField || 'value']


item => item[valueField || 'value']


item => item === origin


item => item[valueField || 'value'] == value[valueField || 'value']


(text: string, index) => {
    text && dom.push({text});
    dom.push(
      <span className={hlClassName} key={`${index}-hl`}


export interface FormOptionsControl extends FormBaseControl {
  /**
   * 选项集合
   */
  options?: Array<Option> | string[] | PlainObject;

  /**
   * 可用来通过 API 拉取 options。
   */
  source?: SchemaApi | SchemaTokenizeableString;

  /**
   * 用表达式来配置 source 接口初始要不要拉取
   *
   * @deprecated 建议用 source 接口的 sendOn
   */
  initFetchOn?: SchemaExpression;

  /**
   * 配置 source 接口初始拉不拉取。
   *
   * @deprecated 建议用 source 接口的 sendOn
   */
  initFetch?: boolean;

  /**
   * 是否为多选模式
   */
  multiple?: boolean;

  /**
   * 单选模式：当用户选中某个选项时，选项中的 value 将被作为该表单项的值提交，否则，整个选项对象都会作为该表单项的值提交。
   * 多选模式：选中的多个选项的 `value` 会通过 `delimiter` 连接起来，否则直接将以数组的形式提交值。
   */
  joinValues?: boolean;

  /**
   * 分割符
   */
  delimiter?: string;

  /**
   * 开启后将选中的选项 value 的值封装为数组，作为当前表单项的值。
   */
  extractValue?: boolean;

  /**
   * 是否可清除。
   */
  clearable?: boolean;

  /**
   * 点清除按钮时，将表单项设置成当前配置的值。
   *
   * @default ''
   */
  resetValue?: string;

  /**
   * 延时加载的 API，当选项中有 defer: true 的选项时，点开会通过此接口扩充。
   */
  deferApi?: SchemaApi;

  /**
   * 添加时调用的接口
   */
  addApi?: SchemaApi;

  /**
   * 新增时的表单项。
   */
  addControls?: Array<FormControlSchema>;

  /**
   * 是否可以新增
   */
  creatable?: boolean;

  /**
   * 新增文字
   */
  createBtnLabel?: string;

  /**
   * 是否可以编辑
   */
  editable?: boolean;

  /**
   * 编辑时调用的 API
   */
  editApi?: SchemaApi;

  /**
   * 选项修改的表单项
   */
  editControls?: Array<FormControlSchema>;

  /**
   * 是否可删除
   */
  removable?: boolean;

  /**
   * 选项删除 API
   */
  deleteApi?: SchemaApi;

  /**
   * 选项删除提示文字。
   */
  deleteConfirmText?: string;

  /**
   * 自动填充，当选项被选择的时候，将选项中的其他值同步设置到表单内。
   */
  autoFill?: {
    [propName: string]: SchemaTokenizeableString;
  };
}


export interface OptionsBasicConfig extends FormItemBasicConfig {
  autoLoadOptionsFromSource?: boolean;
}


export interface OptionsConfig extends OptionsBasicConfig {
  component: React.ComponentType<OptionsControlProps>;
}


// 下发给注册进来的组件的属性。
export interface OptionsControlProps
  extends FormControlProps,
    Omit<FormOptionsControl, 'type'> {
  options: Array<Option>;
  onToggle: (
    option: Option,
    submitOnChange?: boolean,
    changeImmediately?: boolean
  ) => void;
  onToggleAll: () => void;
  selectedOptions: Array<Option>;
  setOptions: (value: Array<any>, skipNormalize?: boolean) => void;
  setLoading: (value: boolean) => void;
  reloadOptions: (setError?: boolean) => void;
  deferLoad: (option: Option) => void;
  onAdd?: (
    idx?: number | Array<number>,
    value?: any,
    skipForm?: boolean
  ) => void;
  onEdit?: (value: Option, origin?: Option, skipForm?: boolean) => void;
  onDelete?: (value: Option) => void;
}


// 自己接收的属性。
export interface OptionsProps extends FormControlProps, OptionProps {
  source?: Api;
  deferApi?: Api;
  creatable?: boolean;
  addApi?: Api;
  addControls?: Array<any>;
  editApi?: Api;
  editControls?: Array<any>;
  deleteApi?: Api;
  deleteConfirmText?: string;
  optionLabel?: string;
}


export type OptionsControlComponent = React.ComponentType<FormControlProps>;


componentWillMount() {
      const {
        initFetch,
        formItem,
        source,
        data,
        setPrinstineValue,
        defaultValue,
        multiple,
        joinValues,
        extractValue,
        addHook,
        formInited,
        valueField,
        options,
        value
      } = this.props;

      if (formItem) {
        formItem.setOptions(normalizeOptions(options));

        this.reaction = reaction(
          () => JSON.stringify([formItem.loading, formItem.filteredOptions]),
          () => this.forceUpdate()
        );
      }

      let loadOptions: boolean = initFetch !== false;

      if (formItem && joinValues === false && defaultValue) {
        const selectedOptions = extractValue
          ? formItem
              .getSelectedOptions(value)
              .map(
                (selectedOption: Option) =>
                  selectedOption[valueField || 'value']
              )
          : formItem.getSelectedOptions(value);
        setPrinstineValue(
          multiple ? selectedOptions.concat() : selectedOptions[0]
        );
      }

      loadOptions &&
        config.autoLoadOptionsFromSource !== false &&
        (formInited
          ? this.reload()
          : addHook && addHook(this.initOptions, 'init'));
    }


componentDidMount() {
      this.normalizeValue();
    }


shouldComponentUpdate(nextProps: OptionsProps) {
      if (config.strictMode === false || nextProps.strictMode === false) {
        return true;
      } else if (nextProps.source || nextProps.autoComplete) {
        return true;
      }

      if (anyChanged(detectProps, this.props, nextProps)) {
        return true;
      }

      return false;
    }


componentDidUpdate(prevProps: OptionsProps) {
      const props = this.props;
      const formItem = props.formItem as IFormItemStore;

      if (!formItem || !props.formInited) {
        return;
      } else if (!prevProps.formItem) {
        // todo 优化 name 变化情况。
      }

      if (prevProps.value !== props.value || formItem.expressionsInOptions) {
        formItem.syncOptions();
      }

      if (prevProps.options !== props.options && formItem) {
        formItem.setOptions(normalizeOptions(props.options || []));
        this.normalizeValue();
      } else if (
        config.autoLoadOptionsFromSource !== false &&
        props.source &&
        formItem &&
        (prevProps.source !== props.source || prevProps.data !== props.data)
      ) {
        if (isPureVariable(props.source as string)) {
          const prevOptions = resolveVariableAndFilter(
            prevProps.source as string,
            prevProps.data,
            '| raw'
          );
          const options = resolveVariableAndFilter(
            props.source as string,
            props.data,
            '| raw'
          );
          prevOptions !== options &&
            formItem.setOptions(normalizeOptions(options || []));

          this.normalizeValue();
        } else if (
          isEffectiveApi(props.source, props.data) &&
          isApiOutdated(
            prevProps.source,
            props.source,
            prevProps.data,
            props.data
          )
        ) {
          formItem
            .loadOptions(
              props.source,
              props.data,
              undefined,
              true,
              props.onChange
            )
            .then(() => this.normalizeValue());
        }
      }
    }


componentWillUnmount() {
      this.props.removeHook && this.props.removeHook(this.reload, 'init');
      this.reaction && this.reaction();
    }


normalizeValue() {
      const {
        joinValues,
        extractValue,
        value,
        multiple,
        formItem,
        valueField
      } = this.props;

      if (!formItem || joinValues !== false || !formItem.options.length) {
        return;
      }

      if (
        extractValue === false &&
        (typeof value === 'string' || typeof value === 'number')
      ) {
        const selectedOptions = formItem.getSelectedOptions(value);
        formItem.changeValue(
          multiple ? selectedOptions.concat() : selectedOptions[0]
        );
      } else if (
        extractValue === true &&
        value &&
        !(
          (Array.isArray(value) &&
            value.every(
              val => typeof val === 'string' || typeof val === 'number'
            )) ||
          typeof value === 'string' ||
          typeof value === 'number'
        )
      ) {
        const selectedOptions = formItem
          .getSelectedOptions(value)
          .map(
            (selectedOption: Option) => selectedOption[valueField || 'value']
          );
        formItem.changeValue(
          multiple ? selectedOptions.concat() : selectedOptions[0]
        );
      }
    }


getWrappedInstance() {
      return this.input;
    }


@autobind
    inputRef(ref: any) {
      this.input = ref;
    }


@autobind
    handleToggle(
      option: Option,
      submitOnChange?: boolean,
      changeImmediately?: boolean
    ) {
      const {
        onChange,
        joinValues,
        extractValue,
        valueField,
        delimiter,
        clearable,
        resetValue,
        multiple,
        formItem,
        value
      } = this.props;

      if (!formItem) {
        return;
      }

      let valueArray = formItem.getSelectedOptions(value).concat();
      const idx = findIndex(
        valueArray,
        optionValueCompare(option[valueField || 'value'], valueField || 'value')
      );
      let newValue: string | Array<Option> | Option = '';

      if (multiple) {
        if (~idx) {
          valueArray.splice(idx, 1);
        } else {
          valueArray.push(option);
        }

        newValue = valueArray;

        if (joinValues) {
          newValue = (newValue as Array<any>)
            .map(item => item[valueField || 'value'])
            .join(delimiter);
        } else if (extractValue) {
          newValue = (newValue as Array<any>).map(
            item => item[valueField || 'value']
          );
        }
      } else {
        if (~idx && clearable) {
          valueArray.splice(idx, 1);
        } else {
          valueArray = [option];
        }

        newValue = valueArray[0] || resetValue;

        if ((joinValues || extractValue) && newValue) {
          newValue = (newValue as any)[valueField || 'value'];
        }
      }

      onChange && onChange(newValue, submitOnChange, changeImmediately);
    }


@autobind
    handleToggleAll() {
      const {
        value,
        onChange,
        joinValues,
        extractValue,
        valueField,
        delimiter,
        resetValue,
        multiple,
        formItem
      } = this.props;

      if (!formItem) {
        return;
      }

      const selectedOptions = formItem.getSelectedOptions(value);
      let valueArray =
        selectedOptions.length === formItem.filteredOptions.length
          ? []
          : formItem.filteredOptions.concat();

      let newValue: string | Array<Option> | Option = '';

      if (multiple) {
        newValue = valueArray;

        if (joinValues) {
          newValue = (newValue as Array<any>)
            .map(item => item[valueField || 'value'])
            .join(delimiter);
        } else if (extractValue) {
          newValue = (newValue as Array<any>).map(
            item => item[valueField || 'value']
          );
        }
      } else {
        newValue = valueArray[0] || resetValue;

        if (joinValues && newValue) {
          newValue = (newValue as any)[valueField || 'value'];
        }
      }

      onChange && onChange(newValue);
    }


// 当有 action 触发，如果指定了 reload 目标组件，有可能会来到这里面来
@autobind
    reload() {
      return this.reloadOptions();
    }


@autobind
    reloadOptions(setError?: boolean) {
      const {source, formItem, data, onChange} = this.props;

      if (formItem && isPureVariable(source as string)) {
        formItem.setOptions(
          normalizeOptions(
            resolveVariableAndFilter(source as string, data, '| raw') || []
          )
        );
        return;
      } else if (!formItem || !isEffectiveApi(source, data)) {
        return;
      }

      return formItem.loadOptions(
        source,
        data,
        undefined,
        false,
        onChange,
        setError
      );
    }


@autobind
    deferLoad(option: Option) {
      const {deferApi, source, env, formItem, data} = this.props;

      if (option.loaded) {
        return;
      }

      const api = option.deferApi || deferApi || source;

      if (!api) {
        env.notify(
          'error',
          '请在选项中设置 `deferApi` 或者表单项中设置 `deferApi`，用来加载子选项。'
        );
        return;
      }

      formItem?.deferLoadOptions(option, api, createObject(data, option));
    }


@autobind
    async initOptions(data: any) {
      await this.reload();
      const {formItem, name} = this.props;
      if (!formItem) {
        return;
      }
      if (isAlive(formItem) && formItem.value) {
        setVariable(data, name!, formItem.value);
      }
    }


focus() {
      this.input && this.input.focus && this.input.focus();
    }


@autobind
    setOptions(options: Array<any>, skipNormalize = false) {
      const formItem = this.props.formItem as IFormItemStore;
      formItem &&
        formItem.setOptions(
          skipNormalize ? options : normalizeOptions(options || [])
        );
    }


@autobind
    syncOptions() {
      const formItem = this.props.formItem as IFormItemStore;
      formItem && formItem.syncOptions();
    }


@autobind
    setLoading(value: boolean) {
      const formItem = this.props.formItem as IFormItemStore;
      formItem && formItem.setLoading(value);
    }


@autobind
    async handleOptionAdd(
      idx: number | Array<number> = -1,
      value: any,
      skipForm: boolean = false
    ) {
      let {
        addControls,
        disabled,
        labelField,
        onOpenDialog,
        optionLabel,
        addApi,
        source,
        data,
        valueField,
        formItem: model,
        createBtnLabel,
        env,
        translate: __
      } = this.props;

      // 禁用或者没有配置 name
      if (disabled || !model) {
        return;
      }

      // 用户没有配置表单项，则自动创建一个 label 输入
      if (!skipForm && (!Array.isArray(addControls) || !addControls.length)) {
        addControls = [
          {
            type: 'text',
            name: labelField || 'label',
            label: false,
            placeholder: __('请输入名称')
          }
        ];
      }
      const ctx = createObject(
        data,
        Array.isArray(idx)
          ? {
              parent: getTree(model.options, idx.slice(0, idx.length - 1)),
              ...value
            }
          : value
      );

      let result: any = skipForm
        ? ctx
        : await onOpenDialog(
            {
              type: 'dialog',
              title: createBtnLabel || `新增${optionLabel || '选项'}`,
              body: {
                type: 'form',
                api: addApi,
                controls: addControls
              }
            },
            ctx
          );

      // 单独发请求
      if (skipForm && addApi) {
        try {
          const payload = await env.fetcher(addApi!, result, {
            method: 'post'
          });

          if (!payload.ok) {
            env.notify('error', payload.msg || '新增失败，请仔细检查');
            result = null;
          } else {
            result = payload.data || result;
          }
        } catch (e) {
          result = null;
          console.error(e);
          env.notify('error', e.message);
        }
      }

      // 有 result 说明弹框点了确认。否则就是取消了。
      if (!result) {
        return;
      }

      // 没走服务端的。
      if (!result.hasOwnProperty(valueField || 'value')) {
        result = {
          ...result,
          [valueField || 'value']: result[labelField || 'label']
        };
      }

      // 如果配置了 source 且配置了 addApi 直接重新拉取接口就够了
      // 不能不判断 addApi 就刷新，因为有些场景就是临时添加的。
      if (source && addApi) {
        this.reload();
      } else {
        // 否则直接前端变更 options
        let options = model.options.concat();
        if (Array.isArray(idx)) {
          options = spliceTree(options, idx, 0, {...result});
        } else {
          ~idx
            ? options.splice(idx, 0, {...result})
            : options.push({...result});
        }
        model.setOptions(options);
      }
    }


@autobind
    async handleOptionEdit(
      value: any,
      origin: any = value,
      skipForm: boolean = false
    ) {
      let {
        editControls,
        disabled,
        labelField,
        onOpenDialog,
        editApi,
        env,
        source,
        data,
        formItem: model,
        optionLabel,
        translate: __
      } = this.props;

      if (disabled || !model) {
        return;
      }

      if (!skipForm && (!Array.isArray(editControls) || !editControls.length)) {
        editControls = [
          {
            type: 'text',
            name: labelField || 'label',
            label: false,
            placeholder: __('请输入名称')
          }
        ];
      }

      let result = skipForm
        ? value
        : await onOpenDialog(
            {
              type: 'dialog',
              title: __('编辑{{label}}', {
                label: optionLabel || '选项'
              }),
              body: {
                type: 'form',
                api: editApi,
                controls: editControls
              }
            },
            createObject(data, value)
          );

      // 单独发请求
      if (skipForm && editApi) {
        try {
          const payload = await env.fetcher(
            editApi!,
            createObject(data, result),
            {
              method: 'post'
            }
          );

          if (!payload.ok) {
            env.notify('error', payload.msg || __('保存失败，请仔细检查'));
            result = null;
          } else {
            result = payload.data || result;
          }
        } catch (e) {
          result = null;
          console.error(e);
          env.notify('error', e.message);
        }
      }

      // 没有结果，说明取消了。
      if (!result) {
        return;
      }

      if (source && !editApi) {
        this.reload();
      } else {
        const indexes = findTreeIndex(model.options, item => item === origin);

        if (indexes) {
          model.setOptions(
            spliceTree(model.options, indexes, 1, {
              ...origin,
              ...result
            })
          );
        }
      }
    }


@autobind
    async handleOptionDelete(value: any) {
      let {
        deleteConfirmText,
        disabled,
        data,
        deleteApi,
        env,
        formItem: model,
        source,
        valueField,
        translate: __
      } = this.props;

      if (disabled || !model) {
        return;
      }

      const ctx = createObject(data, value);

      // 如果配置了 deleteConfirmText 让用户先确认。
      const confirmed = deleteConfirmText
        ? await env.confirm(filter(deleteConfirmText, ctx))
        : true;
      if (!confirmed) {
        return;
      }

      // 通过 deleteApi 删除。
      try {
        if (!deleteApi) {
          throw new Error(__('请配置 deleteApi'));
        }

        const result = await env.fetcher(deleteApi!, ctx, {
          method: 'delete'
        });

        if (!result.ok) {
          env.notify('error', result.msg || __('删除失败，请重试'));
        } else if (source) {
          this.reload();
        } else {
          const options = model.options.concat();
          const idx = findIndex(
            options,
            item => item[valueField || 'value'] == value[valueField || 'value']
          );

          if (~idx) {
            options.splice(idx, 1);
            model.setOptions(options);
          }
        }
      } catch (e) {
        console.error(e);
        env.notify('error', e.message);
      }
    }


render() {
      const {
        value,
        formItem,
        addApi,
        editApi,
        deleteApi,
        creatable,
        editable,
        removable
      } = this.props;

      return (
        <Control
          {...this.props}
          ref={this.inputRef}
          options={formItem ? formItem.filteredOptions : []}
          onToggle={this.handleToggle}
          onToggleAll={this.handleToggleAll}
          selectedOptions={formItem ? formItem.getSelectedOptions(value) : []}
          loading={formItem ? formItem.loading : false}
          setLoading={this.setLoading}
          setOptions={this.setOptions}
          syncOptions={this.syncOptions}
          reloadOptions={this.reload}
          deferLoad={this.deferLoad}
          creatable={
            creatable || (creatable !== false && isEffectiveApi(addApi))
          }


type abbreviation<


@Injectable()
export class UsersService {

  constructor(@Inject(USER_REPOSITORY) private readonly userRepository: typeof User) { }

  async create(user: UserDto): Promise<User> {
    // @ts-ignore
    return await this.userRepository.create<User>(user);
  }

  async findOneByEmailOrPhone(emailOrPhone: string): Promise<User> {
    return await this.userRepository.findOne<User>({
      where: {
        [Op.or]: [{ email: emailOrPhone }, { phone: emailOrPhone }],
      },
    });
  }

  async findOneByEmail(email: string): Promise<User> {
    return await this.userRepository.findOne<User>({ where: { email } });
  }

  async findOneByPhone(phone: string): Promise<User> {
    return await this.userRepository.findOne<User>({ where: { phone } });
  }

  async findOneById(id: number): Promise<User> {
    return await this.userRepository.findOne<User>({ where: { id } });
  }

  async getAll(
    search = '',
    page = 1,
    perPage = 10,
  ): Promise<{ rows: User[]; count: number }> {
    return await this.userRepository.findAndCountAll({
      where: {
        [Op.or]: [
          { name: { [Op.iLike]: `%${search}%` } },
          { email: { [Op.iLike]: `%${search}%` } },
        ],
      },
      offset: (page - 1) * perPage,
      limit: perPage,
    });
  }
}


async create(user: UserDto): Promise<User> {
    // @ts-ignore
    return await this.userRepository.create<User>(user);
  }


async findOneByEmailOrPhone(emailOrPhone: string): Promise<User> {
    return await this.userRepository.findOne<User>({
      where: {
        [Op.or]: [{ email: emailOrPhone }, { phone: emailOrPhone }],
      },
    });
  }


async findOneByEmail(email: string): Promise<User> {
    return await this.userRepository.findOne<User>({ where: { email } });
  }


async findOneByPhone(phone: string): Promise<User> {
    return await this.userRepository.findOne<User>({ where: { phone } });
  }


async findOneById(id: number): Promise<User> {
    return await this.userRepository.findOne<User>({ where: { id } });
  }


async getAll(
    search = '',
    page = 1,
    perPage = 10,
  ): Promise<{ rows: User[]; count: number }> {
    return await this.userRepository.findAndCountAll({
      where: {
        [Op.or]: [
          { name: { [Op.iLike]: `%${search}%` } },
          { email: { [Op.iLike]: `%${search}%` } },
        ],
      },
      offset: (page - 1) * perPage,
      limit: perPage,
    });
  }


(data, ctx: ExecutionContext): User => {
    const req = ctx.switchToHttp().getRequest();
    return req.user;
  }


(exists) => {
			if (exists) {
				// The shortcut already exists, no need to do anything
				return;
			}

			// Create the shortcut
			let properties = {
				target: process.execPath,
				args: path.resolve(__dirname+'/../..'),
				workingDir: path.resolve(__dirname+'/../..'),
				icon: this.faviconIco,
				desc: this.appSlogan,
			};
			console.log(properties);
			ws.create(shortcutPath, properties, (err: any) => {
				if (err) throw err;

				// Add the app ID to the shortcut
				ws.addAppId(shortcutPath, this.appId, (err: any) => {
					if (err) throw err;
					// Ready!
				});
			});
		}


(err: any) => {
				if (err) throw err;

				// Add the app ID to the shortcut
				ws.addAppId(shortcutPath, this.appId, (err: any) => {
					if (err) throw err;
					// Ready!
				});
			}


(err: any) => {
					if (err) throw err;
					// Ready!
				}


export class TrayHandler {

	readonly appId = "eu.nintendo.working-time-alert";

	readonly appName = 'Working Time Alert 2.0';

	favicon: string;

	faviconIco: string;

	app: any;

	window: BrowserWindow;

	store: {
		set(s: string, v: any): void;
		get(s: string): any;
	};

	autoLauncher: any;
	private appSlogan: string = "Never miss the time to go home";

	constructor(app: any, window: BrowserWindow, store: any, favicon: string, faviconIco: string) {
		this.app = app;
		this.window = window;
		this.store = store;
		this.autoLauncher = new AutoLaunch({
			name: this.appName,
		});
		this.favicon = favicon;
		this.faviconIco = faviconIco;
		this.handleTray();
	}

	async handleTray() {
		const appIcon = new Tray(this.favicon);

		const autoStart = await this.autoLauncher.isEnabled();
		const contextMenu = Menu.buildFromTemplate([
			{
				label: 'Toggle Window',
				click: this.toggleWindow.bind(this)
			},
			{
				label: 'Autostart with Windows',
				click: this.toggleAutostart.bind(this),
				type: 'checkbox',
				checked: autoStart,
			},
/*
			{
				label: 'Start visible',
				type: 'checkbox',
				checked: this.store.get('startVisible'),
				click: (e) => {
					// console.log(e);
					this.store.set('startVisible', e.checked);
				}
			},
*/
			{
				label: 'Create Programs Shortcut',
				click: this.createProgramsShortcut.bind(this),
				enabled: !this.hasShortcut(),
			},
			{
				label: 'Quit',
				click: this.quit.bind(this)
			},
		]);
		appIcon.setToolTip(this.appName);
		appIcon.setContextMenu(contextMenu);

		appIcon.on('click', this.toggleWindow.bind(this));

		return appIcon;
	}

	toggleWindow() {
		this.window.isVisible()
			? this.window.hide()
			: this.window.show();
	}

	async toggleAutostart() {
		const enabled = await this.autoLauncher.isEnabled();
		if (enabled) {
			this.autoLauncher.disable();
		} else {
			this.autoLauncher.enable();
		}
	}

	hasShortcut() {
		const shortcutPath = process.env.APPDATA + "\\Microsoft\\Windows\\Start Menu\\Programs\\" + this.appName + ".lnk";
		// console.log(shortcutPath);
		return fs.existsSync(shortcutPath);
	}

	createProgramsShortcut() {
		this.app.setAppUserModelId(this.appId);

		const shortcutPath = process.env.APPDATA + "\\Microsoft\\Windows\\Start Menu\\Programs\\" + this.appName + ".lnk";
		// console.log(shortcutPath);
		fs.exists(shortcutPath,  (exists) => {
			if (exists) {
				// The shortcut already exists, no need to do anything
				return;
			}

			// Create the shortcut
			let properties = {
				target: process.execPath,
				args: path.resolve(__dirname+'/../..'),
				workingDir: path.resolve(__dirname+'/../..'),
				icon: this.faviconIco,
				desc: this.appSlogan,
			};
			console.log(properties);
			ws.create(shortcutPath, properties, (err: any) => {
				if (err) throw err;

				// Add the app ID to the shortcut
				ws.addAppId(shortcutPath, this.appId, (err: any) => {
					if (err) throw err;
					// Ready!
				});
			});
		});
	}

	quit() {
		this.app.quit();
		process.exit();
	}

}


async handleTray() {
		const appIcon = new Tray(this.favicon);

		const autoStart = await this.autoLauncher.isEnabled();
		const contextMenu = Menu.buildFromTemplate([
			{
				label: 'Toggle Window',
				click: this.toggleWindow.bind(this)
			},
			{
				label: 'Autostart with Windows',
				click: this.toggleAutostart.bind(this),
				type: 'checkbox',
				checked: autoStart,
			},
/*
			{
				label: 'Start visible',
				type: 'checkbox',
				checked: this.store.get('startVisible'),
				click: (e) => {
					// console.log(e);
					this.store.set('startVisible', e.checked);
				}
			},
*/
			{
				label: 'Create Programs Shortcut',
				click: this.createProgramsShortcut.bind(this),
				enabled: !this.hasShortcut(),
			},
			{
				label: 'Quit',
				click: this.quit.bind(this)
			},
		]);
		appIcon.setToolTip(this.appName);
		appIcon.setContextMenu(contextMenu);

		appIcon.on('click', this.toggleWindow.bind(this));

		return appIcon;
	}


toggleWindow() {
		this.window.isVisible()
			? this.window.hide()
			: this.window.show();
	}


async toggleAutostart() {
		const enabled = await this.autoLauncher.isEnabled();
		if (enabled) {
			this.autoLauncher.disable();
		} else {
			this.autoLauncher.enable();
		}
	}


hasShortcut() {
		const shortcutPath = process.env.APPDATA + "\\Microsoft\\Windows\\Start Menu\\Programs\\" + this.appName + ".lnk";
		// console.log(shortcutPath);
		return fs.existsSync(shortcutPath);
	}


createProgramsShortcut() {
		this.app.setAppUserModelId(this.appId);

		const shortcutPath = process.env.APPDATA + "\\Microsoft\\Windows\\Start Menu\\Programs\\" + this.appName + ".lnk";
		// console.log(shortcutPath);
		fs.exists(shortcutPath,  (exists) => {
			if (exists) {
				// The shortcut already exists, no need to do anything
				return;
			}

			// Create the shortcut
			let properties = {
				target: process.execPath,
				args: path.resolve(__dirname+'/../..'),
				workingDir: path.resolve(__dirname+'/../..'),
				icon: this.faviconIco,
				desc: this.appSlogan,
			};
			console.log(properties);
			ws.create(shortcutPath, properties, (err: any) => {
				if (err) throw err;

				// Add the app ID to the shortcut
				ws.addAppId(shortcutPath, this.appId, (err: any) => {
					if (err) throw err;
					// Ready!
				});
			});
		});
	}


quit() {
		this.app.quit();
		process.exit();
	}


export default class TelegramConnector
  implements Connector<TelegramRequestBody, TelegramClient> {
  _client: TelegramClient;

  _skipLegacyProfile: boolean;

  constructor(options: ConstructorOptions) {
    const { skipLegacyProfile } = options;
    if ('client' in options) {
      this._client = options.client;
    } else {
      const { accessToken, origin } = options;

      invariant(
        options.accessToken,
        'Telegram access token is required. Please make sure you have filled it correctly in `bottender.config.js` or `.env` file.'
      );

      this._client = TelegramClient.connect({
        accessToken,
        origin,
      });
    }

    this._skipLegacyProfile =
      typeof skipLegacyProfile === 'boolean' ? skipLegacyProfile : true;
  }

  _getRawEventFromRequest(body: TelegramRequestBody): TelegramRawEvent {
    return body;
  }

  get platform(): string {
    return 'telegram';
  }

  get client(): TelegramClient {
    return this._client;
  }

  getUniqueSessionKey(body: TelegramRequestBody): string {
    if (body.message) {
      return `${body.message.chat.id}`;
    }
    if (body.editedMessage) {
      return `${body.editedMessage.chat.id}`;
    }
    if (body.channelPost) {
      return `${body.channelPost.chat.id}`;
    }
    if (body.editedChannelPost) {
      return `${body.editedChannelPost.chat.id}`;
    }
    if (body.inlineQuery) {
      return `${body.inlineQuery.from.id}`;
    }
    if (body.chosenInlineResult) {
      return `${body.chosenInlineResult.from.id}`;
    }
    if (body.callbackQuery) {
      if (body.callbackQuery.message) {
        return `${body.callbackQuery.message.chat.id}`;
      }
      return `${body.callbackQuery.from.id}`;
    }
    if (body.shippingQuery) {
      return `${body.shippingQuery.from.id}`;
    }
    if (body.preCheckoutQuery) {
      return `${body.preCheckoutQuery.from.id}`;
    }
    return '';
  }

  async updateSession(
    session: Session,
    body: TelegramRequestBody
  ): Promise<void> {
    let channel;
    if (body.channelPost) {
      channel = body.channelPost.chat;
    } else if (body.editedChannelPost) {
      channel = body.editedChannelPost.chat;
    }

    if (channel) {
      if (this._skipLegacyProfile) {
        session.channel = {
          id: channel.id,
        };
      } else {
        session.channel = channel;
      }
      session.channel._updatedAt = new Date().toISOString();
      Object.freeze(session.channel);
    }
    Object.defineProperty(session, 'channel', {
      configurable: false,
      enumerable: true,
      writable: false,
      value: session.channel,
    });

    let group;
    if (body.message && body.message.chat.type === 'group') {
      group = body.message.chat;
    } else if (body.editedMessage && body.editedMessage.chat.type === 'group') {
      group = body.editedMessage.chat;
    } else if (
      body.callbackQuery &&
      body.callbackQuery.message &&
      body.callbackQuery.message.chat.type === 'group'
    ) {
      group = body.callbackQuery.message.chat;
    }
    if (group) {
      if (this._skipLegacyProfile) {
        session.group = {
          id: group.id,
        };
      } else {
        session.group = group;
      }
      session.group._updatedAt = new Date().toISOString();
      Object.freeze(session.group);
    }
    Object.defineProperty(session, 'group', {
      configurable: false,
      enumerable: true,
      writable: false,
      value: session.group,
    });

    let user;
    if (body.message) {
      user = body.message.from;
    } else if (body.editedMessage) {
      user = body.editedMessage.from;
    } else if (body.inlineQuery) {
      user = body.inlineQuery.from;
    } else if (body.chosenInlineResult) {
      user = body.chosenInlineResult.from;
    } else if (body.callbackQuery) {
      user = body.callbackQuery.from;
    } else if (body.shippingQuery) {
      user = body.shippingQuery.from;
    } else if (body.preCheckoutQuery) {
      user = body.preCheckoutQuery.from;
    }

    if (user) {
      if (this._skipLegacyProfile) {
        session.user = {
          id: user.id,
        };
      } else {
        session.user = user;
      }
      session.user._updatedAt = new Date().toISOString();
      Object.freeze(session.user);
    }
    Object.defineProperty(session, 'user', {
      configurable: false,
      enumerable: true,
      writable: false,
      value: session.user,
    });
  }

  mapRequestToEvents(body: TelegramRequestBody): TelegramEvent[] {
    const rawEvent = this._getRawEventFromRequest(body);
    return [new TelegramEvent(rawEvent)];
  }

  createContext(params: {
    event: TelegramEvent;
    session: Session | null;
    initialState?: Record<string, any> | null;
    requestContext?: RequestContext;
    emitter?: EventEmitter | null;
  }): TelegramContext {
    return new TelegramContext({
      ...params,
      client: this._client,
    });
  }

  preprocess(): {
    shouldNext: true;
  } {
    return {
      shouldNext: true,
    };
  }
}


export type TelegramRequestBody = TelegramRawEvent;


type ConstructorOptionsWithoutClient = {
  accessToken: string;
  origin?: string;
  skipLegacyProfile?: boolean;
};


type ConstructorOptionsWithClient = {
  client: TelegramClient;
  skipLegacyProfile?: boolean;
};


type ConstructorOptions =
  | ConstructorOptionsWithoutClient
  | ConstructorOptionsWithClient;


_getRawEventFromRequest(body: TelegramRequestBody): TelegramRawEvent {
    return body;
  }


getUniqueSessionKey(body: TelegramRequestBody): string {
    if (body.message) {
      return `${body.message.chat.id}`;
    }
    if (body.editedMessage) {
      return `${body.editedMessage.chat.id}`;
    }
    if (body.channelPost) {
      return `${body.channelPost.chat.id}`;
    }
    if (body.editedChannelPost) {
      return `${body.editedChannelPost.chat.id}`;
    }
    if (body.inlineQuery) {
      return `${body.inlineQuery.from.id}`;
    }
    if (body.chosenInlineResult) {
      return `${body.chosenInlineResult.from.id}`;
    }
    if (body.callbackQuery) {
      if (body.callbackQuery.message) {
        return `${body.callbackQuery.message.chat.id}`;
      }
      return `${body.callbackQuery.from.id}`;
    }
    if (body.shippingQuery) {
      return `${body.shippingQuery.from.id}`;
    }
    if (body.preCheckoutQuery) {
      return `${body.preCheckoutQuery.from.id}`;
    }
    return '';
  }