diff --git a/backend/docs/docs.go b/backend/docs/docs.go index 4dd607b3..bfa1be5e 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -113,7 +113,7 @@ const docTemplate = `{ }, "/assets": { "get": { - "description": "Get all assets with optional pagination", + "description": "Get all assets with optional pagination and filtering", "consumes": [ "application/json" ], @@ -125,15 +125,27 @@ const docTemplate = `{ ], "summary": "Get all assets", "parameters": [ + { + "type": "string", + "description": "Filter by asset name", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "Filter by asset type", + "name": "asset_type", + "in": "query" + }, { "type": "integer", - "description": "Page number", + "description": "Page number for pagination", "name": "page", "in": "query" }, { "type": "integer", - "description": "Number of items per page", + "description": "Number of items per page for pagination", "name": "limit", "in": "query" } @@ -548,6 +560,52 @@ const docTemplate = `{ } } }, + "/assets/update-contract-id": { + "put": { + "description": "Update a Contract ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Assets" + ], + "summary": "Update a Contract ID", + "parameters": [ + { + "description": "Contract ID", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.UpdateContractIdRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.UpdateContractIdRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.response" + } + } + } + } + }, "/assets/update-toml": { "put": { "description": "Update a TOML file", @@ -734,9 +792,111 @@ const docTemplate = `{ } } }, + "/contract/history": { + "put": { + "description": "Update contract history", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contract" + ], + "summary": "Update contract history", + "parameters": [ + { + "description": "History info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.UpdateContractHistoryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/entity.ContractHistory" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.response" + } + } + } + }, + "post": { + "description": "Add contract history", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contract" + ], + "summary": "Add contract history", + "parameters": [ + { + "description": "History info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.AddContractHistoryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/entity.ContractHistory" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.response" + } + } + } + } + }, "/contracts": { "get": { - "description": "Retrieve a list of all contracts, with optional pagination", + "description": "Retrieve a list of history of contract", "consumes": [ "application/json" ], @@ -744,9 +904,9 @@ const docTemplate = `{ "application/json" ], "tags": [ - "Contract" + "ContractHistory" ], - "summary": "Get all contracts", + "summary": "Get contract history", "parameters": [ { "type": "integer", @@ -1671,6 +1831,29 @@ const docTemplate = `{ } } }, + "/users": { + "get": { + "description": "Forget Password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Forget Password", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/entity.UserResponse" + } + } + } + } + }, "/users/edit-users-role": { "post": { "description": "Edit users role", @@ -2312,6 +2495,9 @@ const docTemplate = `{ "type": "string", "example": "USDC" }, + "contract_id": { + "type": "string" + }, "distributor": { "$ref": "#/definitions/entity.Wallet" }, @@ -2320,10 +2506,7 @@ const docTemplate = `{ "example": 1 }, "image": { - "type": "array", - "items": { - "type": "integer" - } + "type": "string" }, "issuer": { "$ref": "#/definitions/entity.Wallet" @@ -2343,6 +2526,9 @@ const docTemplate = `{ "asset": { "$ref": "#/definitions/entity.Asset" }, + "compound": { + "type": "integer" + }, "created_at": { "type": "string" }, @@ -2371,6 +2557,33 @@ const docTemplate = `{ } } }, + "entity.ContractHistory": { + "type": "object", + "properties": { + "contract": { + "$ref": "#/definitions/entity.Contract" + }, + "deposit_amount": { + "type": "number" + }, + "deposited_at": { + "type": "string" + }, + "id": { + "type": "integer", + "example": 1 + }, + "user": { + "$ref": "#/definitions/entity.User" + }, + "withdraw_amount": { + "type": "number" + }, + "withdrawn_at": { + "type": "string" + } + } + }, "entity.Currency": { "type": "object", "properties": { @@ -2803,6 +3016,12 @@ const docTemplate = `{ }, "updated_at": { "type": "string" + }, + "vault": { + "$ref": "#/definitions/entity.Vault" + }, + "vault_id": { + "type": "integer" } } }, @@ -2851,9 +3070,15 @@ const docTemplate = `{ "type": "string", "example": "Treasury" }, + "owner_id": { + "type": "integer" + }, "vault_category": { "$ref": "#/definitions/entity.VaultCategory" }, + "vault_category_id": { + "type": "integer" + }, "wallet": { "$ref": "#/definitions/entity.Wallet" } @@ -2895,6 +3120,9 @@ const docTemplate = `{ } } }, + "v1.AddContractHistoryRequest": { + "type": "object" + }, "v1.BurnAssetRequest": { "type": "object", "required": [ @@ -3028,6 +3256,10 @@ const docTemplate = `{ "type": "string", "example": "1" }, + "compound": { + "type": "integer", + "example": 1 + }, "min_deposit": { "type": "integer", "example": 1 @@ -3042,7 +3274,7 @@ const docTemplate = `{ }, "term": { "type": "integer", - "example": 1 + "example": 60 }, "vault_id": { "type": "string", @@ -3074,8 +3306,7 @@ const docTemplate = `{ "type": "object", "required": [ "assets_id", - "name", - "vault_category_id" + "name" ], "properties": { "assets_id": { @@ -3088,6 +3319,9 @@ const docTemplate = `{ "type": "string", "example": "Treasury" }, + "owner_id": { + "type": "integer" + }, "vault_category_id": { "type": "integer", "example": 1 @@ -3295,6 +3529,18 @@ const docTemplate = `{ } } }, + "v1.UpdateContractHistoryRequest": { + "type": "object" + }, + "v1.UpdateContractIdRequest": { + "type": "object", + "properties": { + "contract_id": { + "type": "string", + "example": "iVBORw0KGgoAAAANSUhEUgAACqoAAAMMCAMAAAAWqpRaAAADAFBMVEX///..." + } + } + }, "v1.UpdateVaultAssetRequest": { "type": "object", "required": [ diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 1efebc25..1abb59ae 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -102,7 +102,7 @@ }, "/assets": { "get": { - "description": "Get all assets with optional pagination", + "description": "Get all assets with optional pagination and filtering", "consumes": [ "application/json" ], @@ -114,15 +114,27 @@ ], "summary": "Get all assets", "parameters": [ + { + "type": "string", + "description": "Filter by asset name", + "name": "name", + "in": "query" + }, + { + "type": "string", + "description": "Filter by asset type", + "name": "asset_type", + "in": "query" + }, { "type": "integer", - "description": "Page number", + "description": "Page number for pagination", "name": "page", "in": "query" }, { "type": "integer", - "description": "Number of items per page", + "description": "Number of items per page for pagination", "name": "limit", "in": "query" } @@ -537,6 +549,52 @@ } } }, + "/assets/update-contract-id": { + "put": { + "description": "Update a Contract ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Assets" + ], + "summary": "Update a Contract ID", + "parameters": [ + { + "description": "Contract ID", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.UpdateContractIdRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/v1.UpdateContractIdRequest" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.response" + } + } + } + } + }, "/assets/update-toml": { "put": { "description": "Update a TOML file", @@ -723,9 +781,111 @@ } } }, + "/contract/history": { + "put": { + "description": "Update contract history", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contract" + ], + "summary": "Update contract history", + "parameters": [ + { + "description": "History info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.UpdateContractHistoryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/entity.ContractHistory" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.response" + } + } + } + }, + "post": { + "description": "Add contract history", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Contract" + ], + "summary": "Add contract history", + "parameters": [ + { + "description": "History info", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/v1.AddContractHistoryRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/entity.ContractHistory" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/v1.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/v1.response" + } + } + } + } + }, "/contracts": { "get": { - "description": "Retrieve a list of all contracts, with optional pagination", + "description": "Retrieve a list of history of contract", "consumes": [ "application/json" ], @@ -733,9 +893,9 @@ "application/json" ], "tags": [ - "Contract" + "ContractHistory" ], - "summary": "Get all contracts", + "summary": "Get contract history", "parameters": [ { "type": "integer", @@ -1660,6 +1820,29 @@ } } }, + "/users": { + "get": { + "description": "Forget Password", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Forget Password", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/entity.UserResponse" + } + } + } + } + }, "/users/edit-users-role": { "post": { "description": "Edit users role", @@ -2301,6 +2484,9 @@ "type": "string", "example": "USDC" }, + "contract_id": { + "type": "string" + }, "distributor": { "$ref": "#/definitions/entity.Wallet" }, @@ -2309,10 +2495,7 @@ "example": 1 }, "image": { - "type": "array", - "items": { - "type": "integer" - } + "type": "string" }, "issuer": { "$ref": "#/definitions/entity.Wallet" @@ -2332,6 +2515,9 @@ "asset": { "$ref": "#/definitions/entity.Asset" }, + "compound": { + "type": "integer" + }, "created_at": { "type": "string" }, @@ -2360,6 +2546,33 @@ } } }, + "entity.ContractHistory": { + "type": "object", + "properties": { + "contract": { + "$ref": "#/definitions/entity.Contract" + }, + "deposit_amount": { + "type": "number" + }, + "deposited_at": { + "type": "string" + }, + "id": { + "type": "integer", + "example": 1 + }, + "user": { + "$ref": "#/definitions/entity.User" + }, + "withdraw_amount": { + "type": "number" + }, + "withdrawn_at": { + "type": "string" + } + } + }, "entity.Currency": { "type": "object", "properties": { @@ -2792,6 +3005,12 @@ }, "updated_at": { "type": "string" + }, + "vault": { + "$ref": "#/definitions/entity.Vault" + }, + "vault_id": { + "type": "integer" } } }, @@ -2840,9 +3059,15 @@ "type": "string", "example": "Treasury" }, + "owner_id": { + "type": "integer" + }, "vault_category": { "$ref": "#/definitions/entity.VaultCategory" }, + "vault_category_id": { + "type": "integer" + }, "wallet": { "$ref": "#/definitions/entity.Wallet" } @@ -2884,6 +3109,9 @@ } } }, + "v1.AddContractHistoryRequest": { + "type": "object" + }, "v1.BurnAssetRequest": { "type": "object", "required": [ @@ -3017,6 +3245,10 @@ "type": "string", "example": "1" }, + "compound": { + "type": "integer", + "example": 1 + }, "min_deposit": { "type": "integer", "example": 1 @@ -3031,7 +3263,7 @@ }, "term": { "type": "integer", - "example": 1 + "example": 60 }, "vault_id": { "type": "string", @@ -3063,8 +3295,7 @@ "type": "object", "required": [ "assets_id", - "name", - "vault_category_id" + "name" ], "properties": { "assets_id": { @@ -3077,6 +3308,9 @@ "type": "string", "example": "Treasury" }, + "owner_id": { + "type": "integer" + }, "vault_category_id": { "type": "integer", "example": 1 @@ -3284,6 +3518,18 @@ } } }, + "v1.UpdateContractHistoryRequest": { + "type": "object" + }, + "v1.UpdateContractIdRequest": { + "type": "object", + "properties": { + "contract_id": { + "type": "string", + "example": "iVBORw0KGgoAAAANSUhEUgAACqoAAAMMCAMAAAAWqpRaAAADAFBMVEX///..." + } + } + }, "v1.UpdateVaultAssetRequest": { "type": "object", "required": [ diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 663e3455..f9e21445 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -9,15 +9,15 @@ definitions: code: example: USDC type: string + contract_id: + type: string distributor: $ref: '#/definitions/entity.Wallet' id: example: 1 type: integer image: - items: - type: integer - type: array + type: string issuer: $ref: '#/definitions/entity.Wallet' name: @@ -30,6 +30,8 @@ definitions: type: string asset: $ref: '#/definitions/entity.Asset' + compound: + type: integer created_at: type: string id: @@ -49,6 +51,24 @@ definitions: yield_rate: type: integer type: object + entity.ContractHistory: + properties: + contract: + $ref: '#/definitions/entity.Contract' + deposit_amount: + type: number + deposited_at: + type: string + id: + example: 1 + type: integer + user: + $ref: '#/definitions/entity.User' + withdraw_amount: + type: number + withdrawn_at: + type: string + type: object entity.Currency: properties: anchor_asset: @@ -342,6 +362,10 @@ definitions: type: integer updated_at: type: string + vault: + $ref: '#/definitions/entity.Vault' + vault_id: + type: integer type: object entity.UserRole: properties: @@ -373,8 +397,12 @@ definitions: name: example: Treasury type: string + owner_id: + type: integer vault_category: $ref: '#/definitions/entity.VaultCategory' + vault_category_id: + type: integer wallet: $ref: '#/definitions/entity.Wallet' type: object @@ -403,6 +431,8 @@ definitions: example: sponsor type: string type: object + v1.AddContractHistoryRequest: + type: object v1.BurnAssetRequest: properties: amount: @@ -493,6 +523,9 @@ definitions: asset_id: example: "1" type: string + compound: + example: 1 + type: integer min_deposit: example: 1 type: integer @@ -503,7 +536,7 @@ definitions: example: 1 type: integer term: - example: 1 + example: 60 type: integer vault_id: example: "1" @@ -541,13 +574,14 @@ definitions: name: example: Treasury type: string + owner_id: + type: integer vault_category_id: example: 1 type: integer required: - assets_id - name - - vault_category_id type: object v1.CreateWalletRequest: properties: @@ -691,6 +725,14 @@ definitions: - code - issuer type: object + v1.UpdateContractHistoryRequest: + type: object + v1.UpdateContractIdRequest: + properties: + contract_id: + example: iVBORw0KGgoAAAANSUhEUgAACqoAAAMMCAMAAAAWqpRaAAADAFBMVEX///... + type: string + type: object v1.UpdateVaultAssetRequest: properties: asset_code: @@ -802,13 +844,21 @@ paths: get: consumes: - application/json - description: Get all assets with optional pagination + description: Get all assets with optional pagination and filtering parameters: - - description: Page number + - description: Filter by asset name + in: query + name: name + type: string + - description: Filter by asset type + in: query + name: asset_type + type: string + - description: Page number for pagination in: query name: page type: integer - - description: Number of items per page + - description: Number of items per page for pagination in: query name: limit type: integer @@ -1142,6 +1192,36 @@ paths: summary: Transfer an asset tags: - Assets + /assets/update-contract-id: + put: + consumes: + - application/json + description: Update a Contract ID + parameters: + - description: Contract ID + in: body + name: request + required: true + schema: + $ref: '#/definitions/v1.UpdateContractIdRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/v1.UpdateContractIdRequest' + "400": + description: Bad Request + schema: + $ref: '#/definitions/v1.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/v1.response' + summary: Update a Contract ID + tags: + - Assets /assets/update-toml: put: consumes: @@ -1206,11 +1286,78 @@ paths: summary: Create a new contract tags: - Contract + /contract/history: + post: + consumes: + - application/json + description: Add contract history + parameters: + - description: History info + in: body + name: request + required: true + schema: + $ref: '#/definitions/v1.AddContractHistoryRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/entity.ContractHistory' + "400": + description: Bad Request + schema: + $ref: '#/definitions/v1.response' + "404": + description: Not Found + schema: + $ref: '#/definitions/v1.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/v1.response' + summary: Add contract history + tags: + - Contract + put: + consumes: + - application/json + description: Update contract history + parameters: + - description: History info + in: body + name: request + required: true + schema: + $ref: '#/definitions/v1.UpdateContractHistoryRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/entity.ContractHistory' + "400": + description: Bad Request + schema: + $ref: '#/definitions/v1.response' + "404": + description: Not Found + schema: + $ref: '#/definitions/v1.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/v1.response' + summary: Update contract history + tags: + - Contract /contracts: get: consumes: - application/json - description: Retrieve a list of all contracts, with optional pagination + description: Retrieve a list of history of contract parameters: - description: Page number for pagination in: query @@ -1237,9 +1384,9 @@ paths: description: Internal server error schema: $ref: '#/definitions/v1.response' - summary: Get all contracts + summary: Get contract history tags: - - Contract + - ContractHistory /log_transactions: get: consumes: @@ -1819,6 +1966,21 @@ paths: summary: Logout User tags: - user + /users: + get: + consumes: + - application/json + description: Forget Password + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/entity.UserResponse' + summary: Forget Password + tags: + - user /users/edit-users-role: post: consumes: diff --git a/backend/internal/controller/http/v1/assets.go b/backend/internal/controller/http/v1/assets.go index 0e7442b4..687861fe 100644 --- a/backend/internal/controller/http/v1/assets.go +++ b/backend/internal/controller/http/v1/assets.go @@ -288,6 +288,16 @@ func (r *assetsRoutes) createAsset(c *gin.Context) { } } + for _, flag := range request.SetFlags { + switch flag { + case "AUTH_REQUIRED_FLAG": + asset.AuthorizeRequired = true + case "AUTH_CLAWBACK_ENABLED": + asset.ClawbackEnabled = true + case "AUTH_REVOCABLE_FLAG": + asset.FreezeEnabled = true + } + } asset, err = r.as.Create(asset, imageBytes) if err != nil { r.logger.Error(err, "http - v1 - create asset - create asset") @@ -860,12 +870,14 @@ func (r *assetsRoutes) updateAuthFlags(c *gin.Context) { } // @Summary Get all assets -// @Description Get all assets with optional pagination +// @Description Get all assets with optional pagination and filtering // @Tags Assets // @Accept json // @Produce json -// @Param page query int false "Page number" -// @Param limit query int false "Number of items per page" +// @Param name query string false "Filter by asset name" +// @Param asset_type query string false "Filter by asset type" +// @Param page query int false "Page number for pagination" +// @Param limit query int false "Number of items per page for pagination" // @Success 200 {object} PaginatedAssetsResponse // @Failure 500 {object} response // @Router /assets [get] @@ -873,6 +885,39 @@ func (r *assetsRoutes) getAllAssets(c *gin.Context) { pageQuery := c.Query("page") limitQuery := c.Query("limit") + // Parse additional filter parameters + nameFilter := c.Query("name") + assetTypeFilter := c.Query("asset_type") + + authorizeRequired, err := parseBoolQueryParameter(c.Query("authorize_required")) + if err != nil { + r.logger.Error(err, "http - v1 - get all assets - parse authorize_required") + errorResponse(c, http.StatusBadRequest, "Invalid authorize_required parameter", err) + return + } + + clawbackEnabled, err := parseBoolQueryParameter(c.Query("clawback_enabled")) + if err != nil { + r.logger.Error(err, "http - v1 - get all assets - parse clawback_enabled") + errorResponse(c, http.StatusBadRequest, "Invalid clawback_enabled parameter", err) + return + } + + freezeEnabled, err := parseBoolQueryParameter(c.Query("freeze_enabled")) + if err != nil { + r.logger.Error(err, "http - v1 - get all assets - parse freeze_enabled") + errorResponse(c, http.StatusBadRequest, "Invalid freeze_enabled parameter", err) + return + } + + filter := entity.AssetFilter{ + AssetName: nameFilter, + AssetType: assetTypeFilter, + AuthorizeRequired: authorizeRequired, + ClawbackEnabled: clawbackEnabled, + FreezeEnabled: freezeEnabled, + } + if pageQuery != "" && limitQuery != "" { // Parse query parameters for pagination page, err := strconv.Atoi(pageQuery) @@ -889,7 +934,7 @@ func (r *assetsRoutes) getAllAssets(c *gin.Context) { } // Fetch paginated assets - assets, totalPages, err := r.as.GetPaginatedAssets(page, limit) + assets, totalPages, err := r.as.GetPaginatedAssets(page, limit, filter) if err != nil { r.logger.Error(err, "http - v1 - get all assets - get paginated") errorResponse(c, http.StatusInternalServerError, "error getting paginated assets", err) @@ -902,7 +947,7 @@ func (r *assetsRoutes) getAllAssets(c *gin.Context) { }) } else { // Fetch all assets - assets, err := r.as.GetAll() + assets, err := r.as.GetAll(filter) if err != nil { r.logger.Error(err, "http - v1 - get all assets - get all") errorResponse(c, http.StatusInternalServerError, "error getting all assets", err) @@ -1099,8 +1144,8 @@ func (r *assetsRoutes) getTomlData(c *gin.Context) { // @Tags Assets // @Accept json // @Produce json -// @Param request body entity.UpdateContractIdRequest true "Contract ID" -// @Success 200 {object} entity.UpdateContractIdRequest +// @Param request body UpdateContractIdRequest true "Contract ID" +// @Success 200 {object} UpdateContractIdRequest // @Failure 400 {object} response // @Failure 500 {object} response // @Router /assets/update-contract-id [put] diff --git a/backend/internal/controller/http/v1/users.go b/backend/internal/controller/http/v1/users.go index 3f8da692..c830849b 100644 --- a/backend/internal/controller/http/v1/users.go +++ b/backend/internal/controller/http/v1/users.go @@ -242,7 +242,7 @@ func (r *usersRoutes) getProfile(c *gin.Context) { // @Tags user // @Accept json // @Produce json -// @Success 200 {object} entity. +// @Success 200 {object} entity.UserResponse // @Router /users [get] func (r *usersRoutes) forgetPassword(c *gin.Context) { } diff --git a/backend/internal/controller/http/v1/utils.go b/backend/internal/controller/http/v1/utils.go index b2fd1b6e..eb8dceb4 100644 --- a/backend/internal/controller/http/v1/utils.go +++ b/backend/internal/controller/http/v1/utils.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "strconv" "strings" "time" @@ -121,3 +122,15 @@ func generateID() int { currentTimeInMillis := int(time.Now().UnixNano() / int64(time.Millisecond)) return currentTimeInMillis } + +// Helper function to parse string to *bool +func parseBoolQueryParameter(value string) (*bool, error) { + if value == "" { + return nil, nil + } + boolValue, err := strconv.ParseBool(value) + if err != nil { + return nil, err + } + return &boolValue, nil +} diff --git a/backend/internal/entity/asset.go b/backend/internal/entity/asset.go index 82d8ab3f..a8facbfa 100644 --- a/backend/internal/entity/asset.go +++ b/backend/internal/entity/asset.go @@ -1,15 +1,18 @@ package entity type Asset struct { - Id int `json:"id" example:"1"` - Name string `json:"name" example:"USD Coin"` - Code string `json:"code" example:"USDC"` - Distributor Wallet `json:"distributor"` - Issuer Wallet `json:"issuer"` - Amount int `json:"amount" example:"1000000"` - AssetType string `json:"asset_type"` - Image string `json:"image,omitempty"` - ContractId *string `json:"contract_id,omitempty"` + Id int `json:"id" example:"1"` + Name string `json:"name" example:"USD Coin"` + Code string `json:"code" example:"USDC"` + Distributor Wallet `json:"distributor"` + Issuer Wallet `json:"issuer"` + Amount int `json:"amount" example:"1000000"` + AssetType string `json:"asset_type"` + Image string `json:"image,omitempty"` + ContractId *string `json:"contract_id,omitempty"` + AuthorizeRequired bool `json:"authorize_required" example:"true"` + ClawbackEnabled bool `json:"clawback_enabled" example:"false"` + FreezeEnabled bool `json:"freeze_enabled" example:"false"` } const ( @@ -98,3 +101,11 @@ type Toml struct { CreatedAt string `pg:"default:now()"` UpdatedAt string `pg:"default:now()"` } + +type AssetFilter struct { + AssetName string + AssetType string + AuthorizeRequired *bool + ClawbackEnabled *bool + FreezeEnabled *bool +} diff --git a/backend/internal/entity/mocks/mocks.go b/backend/internal/entity/mocks/mocks.go index b8365978..ade9f910 100644 --- a/backend/internal/entity/mocks/mocks.go +++ b/backend/internal/entity/mocks/mocks.go @@ -1,8 +1,8 @@ // Code generated by MockGen. DO NOT EDIT. // Source: internal/entity/interfaces.go -// Package mocks is a generated GoMock package. -package mocks +// Package mock_entity is a generated GoMock package. +package mock_entity import ( reflect "reflect" diff --git a/backend/internal/usecase/assets.go b/backend/internal/usecase/assets.go index 0d528112..0bd9eeb4 100644 --- a/backend/internal/usecase/assets.go +++ b/backend/internal/usecase/assets.go @@ -78,8 +78,8 @@ func (uc *AssetUseCase) GetById(id string) (entity.Asset, error) { return asset, nil } -func (uc *AssetUseCase) GetAll() ([]entity.Asset, error) { - assets, err := uc.aRepo.GetAssets() +func (uc *AssetUseCase) GetAll(filter entity.AssetFilter) ([]entity.Asset, error) { + assets, err := uc.aRepo.GetAssets(filter) if err != nil { return nil, fmt.Errorf("AssetUseCase - GetAll - uc.repo.GetAssets: %w", err) } @@ -190,8 +190,8 @@ func (uc *AssetUseCase) UpdateContractId(assetId string, contractId string) erro return nil } -func (uc *AssetUseCase) GetPaginatedAssets(page int, limit int) ([]entity.Asset, int, error) { - assets, totalPages, err := uc.aRepo.GetPaginatedAssets(page, limit) +func (uc *AssetUseCase) GetPaginatedAssets(page int, limit int, filter entity.AssetFilter) ([]entity.Asset, int, error) { + assets, totalPages, err := uc.aRepo.GetPaginatedAssets(page, limit, filter) if err != nil { return nil, 0, fmt.Errorf("AssetUseCase - GetPaginated - uc.repo.GetPaginated: %w", err) } diff --git a/backend/internal/usecase/assets_test.go b/backend/internal/usecase/assets_test.go index 03514199..98498873 100644 --- a/backend/internal/usecase/assets_test.go +++ b/backend/internal/usecase/assets_test.go @@ -215,6 +215,8 @@ func TestAssetUseCaseCreate(t *testing.T) { mockAsset, } + mockFilter := entity.AssetFilter{} + mockToml := "mock toml data" mockReq := entity.TomlData{ Version: "2.0.0", @@ -245,7 +247,7 @@ func TestAssetUseCaseCreate(t *testing.T) { { name: "get all - success", mock: func() { - ra.EXPECT().GetAssets().Return(mockAssets, nil) + ra.EXPECT().GetAssets(mockFilter).Return(mockAssets, nil) }, res: mockAssets, err: nil, @@ -253,7 +255,7 @@ func TestAssetUseCaseCreate(t *testing.T) { { name: "get all - database error", mock: func() { - ra.EXPECT().GetAssets().Return([]entity.Asset{}, assetDbError) + ra.EXPECT().GetAssets(mockFilter).Return([]entity.Asset{}, assetDbError) }, res: []entity.Asset{}, err: assetDbError, @@ -300,7 +302,7 @@ func TestAssetUseCaseCreate(t *testing.T) { // Test GetAll if _, ok := tc.req.(bool); ok { - res, err := u.GetAll() + res, err := u.GetAll(mockFilter) require.EqualValues(t, tc.res, res) if tc.err == nil { require.EqualValues(t, err, tc.err) diff --git a/backend/internal/usecase/interfaces.go b/backend/internal/usecase/interfaces.go index bd8bbfe5..73862255 100644 --- a/backend/internal/usecase/interfaces.go +++ b/backend/internal/usecase/interfaces.go @@ -43,13 +43,13 @@ type ( // Asset -. AssetRepoInterface interface { GetAsset(int) (entity.Asset, error) - GetAssets() ([]entity.Asset, error) + GetAssets(entity.AssetFilter) ([]entity.Asset, error) GetAssetByCode(string) (entity.Asset, error) CreateAsset(entity.Asset) (entity.Asset, error) GetAssetById(string) (entity.Asset, error) StoreAssetImage(string, string) error GetAssetImage(string) ([]byte, error) - GetPaginatedAssets(int, int) ([]entity.Asset, int, error) + GetPaginatedAssets(int, int, entity.AssetFilter) ([]entity.Asset, int, error) UpdateContractId(string, string) error } diff --git a/backend/internal/usecase/mocks/mocks.go b/backend/internal/usecase/mocks/mocks.go index 5033d321..7d61df74 100644 --- a/backend/internal/usecase/mocks/mocks.go +++ b/backend/internal/usecase/mocks/mocks.go @@ -447,24 +447,24 @@ func (mr *MockAssetRepoInterfaceMockRecorder) GetAssetImage(arg0 interface{}) *g } // GetAssets mocks base method. -func (m *MockAssetRepoInterface) GetAssets() ([]entity.Asset, error) { +func (m *MockAssetRepoInterface) GetAssets(arg0 entity.AssetFilter) ([]entity.Asset, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetAssets") + ret := m.ctrl.Call(m, "GetAssets", arg0) ret0, _ := ret[0].([]entity.Asset) ret1, _ := ret[1].(error) return ret0, ret1 } // GetAssets indicates an expected call of GetAssets. -func (mr *MockAssetRepoInterfaceMockRecorder) GetAssets() *gomock.Call { +func (mr *MockAssetRepoInterfaceMockRecorder) GetAssets(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAssets", reflect.TypeOf((*MockAssetRepoInterface)(nil).GetAssets)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAssets", reflect.TypeOf((*MockAssetRepoInterface)(nil).GetAssets), arg0) } // GetPaginatedAssets mocks base method. -func (m *MockAssetRepoInterface) GetPaginatedAssets(arg0, arg1 int) ([]entity.Asset, int, error) { +func (m *MockAssetRepoInterface) GetPaginatedAssets(arg0, arg1 int, arg2 entity.AssetFilter) ([]entity.Asset, int, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetPaginatedAssets", arg0, arg1) + ret := m.ctrl.Call(m, "GetPaginatedAssets", arg0, arg1, arg2) ret0, _ := ret[0].([]entity.Asset) ret1, _ := ret[1].(int) ret2, _ := ret[2].(error) @@ -472,9 +472,9 @@ func (m *MockAssetRepoInterface) GetPaginatedAssets(arg0, arg1 int) ([]entity.As } // GetPaginatedAssets indicates an expected call of GetPaginatedAssets. -func (mr *MockAssetRepoInterfaceMockRecorder) GetPaginatedAssets(arg0, arg1 interface{}) *gomock.Call { +func (mr *MockAssetRepoInterfaceMockRecorder) GetPaginatedAssets(arg0, arg1, arg2 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPaginatedAssets", reflect.TypeOf((*MockAssetRepoInterface)(nil).GetPaginatedAssets), arg0, arg1) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPaginatedAssets", reflect.TypeOf((*MockAssetRepoInterface)(nil).GetPaginatedAssets), arg0, arg1, arg2) } // StoreAssetImage mocks base method. diff --git a/backend/internal/usecase/repo/asset_postgres.go b/backend/internal/usecase/repo/asset_postgres.go index c1172eee..64ea0cc1 100644 --- a/backend/internal/usecase/repo/asset_postgres.go +++ b/backend/internal/usecase/repo/asset_postgres.go @@ -3,6 +3,8 @@ package repo import ( "database/sql" "fmt" + "strconv" + "strings" "github.com/CheesecakeLabs/token-factory-v2/backend/internal/entity" "github.com/CheesecakeLabs/token-factory-v2/backend/pkg/postgres" @@ -34,31 +36,61 @@ func (r AssetRepo) GetAsset(id int) (entity.Asset, error) { return asset, nil } -func (r AssetRepo) GetAssets() ([]entity.Asset, error) { - query := ` +func (r AssetRepo) GetAssets(filter entity.AssetFilter) ([]entity.Asset, error) { + queryBuilder := ` SELECT - a.id AS asset_id, a.name AS asset_name, a.asset_type, a.code AS code, + a.id, a.name, a.asset_type, a.code, COALESCE(a.image, '') AS image, a.contract_id, - d.id AS distributor_id, d.type AS distributor_type, d.funded AS distributor_funded, - dk.id AS distributor_key_id, dk.public_key AS distributor_key_public_key, dk.weight AS distributor_key_weight, - i.id AS issuer_id, i.type AS issuer_type, i.funded AS issuer_funded, - ik.id AS issuer_key_id, ik.public_key AS issuer_key_public_key, ik.weight AS issuer_key_weight + a.authorize_required, a.clawback_enabled, a.freeze_enabled, + d.id, d.type, d.funded, + dk.id, dk.public_key, dk.weight, + i.id, i.type, i.funded, + ik.id, ik.public_key, ik.weight FROM asset a JOIN wallet d ON a.distributor_id = d.id JOIN key dk ON d.id = dk.wallet_id JOIN wallet i ON a.issuer_id = i.id JOIN key ik ON i.id = ik.wallet_id - ORDER BY a.name; ` - rows, err := r.Db.Query(query) + var args []interface{} + var conditions []string + + if filter.AssetName != "" { + conditions = append(conditions, "a.name = $"+strconv.Itoa(len(args)+1)) + args = append(args, filter.AssetName) + } + if filter.AssetType != "" { + conditions = append(conditions, "a.asset_type = $"+strconv.Itoa(len(args)+1)) + args = append(args, filter.AssetType) + } + if filter.AuthorizeRequired != nil { + conditions = append(conditions, "a.authorize_required = $"+strconv.Itoa(len(args)+1)) + args = append(args, *filter.AuthorizeRequired) + } + if filter.ClawbackEnabled != nil { + conditions = append(conditions, "a.clawback_enabled = $"+strconv.Itoa(len(args)+1)) + args = append(args, *filter.ClawbackEnabled) + } + if filter.FreezeEnabled != nil { + conditions = append(conditions, "a.freeze_enabled = $"+strconv.Itoa(len(args)+1)) + args = append(args, *filter.FreezeEnabled) + } + + if len(conditions) > 0 { + queryBuilder += " WHERE " + strings.Join(conditions, " AND ") + } + + queryBuilder += " ORDER BY a.name;" + + // Execute the query + rows, err := r.Db.Query(queryBuilder, args...) if err != nil { - return nil, fmt.Errorf("AssetRepo - GetAssets - Query: %w", err) + return nil, fmt.Errorf("AssetRepo - GetAll - Query: %w", err) } defer rows.Close() var assets []entity.Asset - for rows.Next() { var asset entity.Asset var distributor entity.Wallet @@ -68,13 +100,14 @@ func (r AssetRepo) GetAssets() ([]entity.Asset, error) { err := rows.Scan( &asset.Id, &asset.Name, &asset.AssetType, &asset.Code, &image, &asset.ContractId, + &asset.AuthorizeRequired, &asset.ClawbackEnabled, &asset.FreezeEnabled, &distributor.Id, &distributor.Type, &distributor.Funded, &distributor.Key.Id, &distributor.Key.PublicKey, &distributor.Key.Weight, &issuer.Id, &issuer.Type, &issuer.Funded, &issuer.Key.Id, &issuer.Key.PublicKey, &issuer.Key.Weight, ) if err != nil { - return nil, fmt.Errorf("AssetRepo - GetAssets - row.Scan: %w", err) + return nil, fmt.Errorf("AssetRepo - GetAll - row.Scan: %w", err) } if image.Valid { @@ -194,44 +227,63 @@ func (r AssetRepo) GetAssetById(id string) (entity.Asset, error) { return asset, nil } -func (r AssetRepo) GetPaginatedAssets(page int, limit int) ([]entity.Asset, int, error) { - // Calculate the offset +func (r AssetRepo) GetPaginatedAssets(page int, limit int, filter entity.AssetFilter) ([]entity.Asset, int, error) { // Calculate the offset offset := (page - 1) * limit - // Query to count the total number of assets - countQuery := ` - SELECT COUNT(*) - FROM asset; + // Start building the SQL query + queryBuilder := ` + SELECT + a.id, a.name, a.asset_type, a.code, + COALESCE(a.image, '') AS image, a.contract_id, + a.authorize_required, a.clawback_enabled, a.freeze_enabled, + d.id, d.type, d.funded, + dk.id, dk.public_key, dk.weight, + i.id, i.type, i.funded, + ik.id, ik.public_key, ik.weight + FROM asset a + JOIN wallet d ON a.distributor_id = d.id + JOIN key dk ON d.id = dk.wallet_id + JOIN wallet i ON a.issuer_id = i.id + JOIN key ik ON i.id = ik.wallet_id ` - var totalAssets int - err := r.Db.QueryRow(countQuery).Scan(&totalAssets) - if err != nil { - return nil, 0, fmt.Errorf("AssetRepo - GetPaginated - Count Query: %w", err) + + var args []interface{} + var conditions []string + + // Existing filter conditions + if filter.AssetName != "" { + conditions = append(conditions, "a.name = $"+strconv.Itoa(len(args)+1)) + args = append(args, filter.AssetName) + } + if filter.AssetType != "" { + conditions = append(conditions, "a.asset_type = $"+strconv.Itoa(len(args)+1)) + args = append(args, filter.AssetType) } - // Calculate total pages - totalPages := (totalAssets + limit - 1) / limit + // New filter conditions for control flags + if filter.AuthorizeRequired != nil { + conditions = append(conditions, "a.authorize_required = $"+strconv.Itoa(len(args)+1)) + args = append(args, *filter.AuthorizeRequired) + } + if filter.ClawbackEnabled != nil { + conditions = append(conditions, "a.clawback_enabled = $"+strconv.Itoa(len(args)+1)) + args = append(args, *filter.ClawbackEnabled) + } + if filter.FreezeEnabled != nil { + conditions = append(conditions, "a.freeze_enabled = $"+strconv.Itoa(len(args)+1)) + args = append(args, *filter.FreezeEnabled) + } - // Query to fetch paginated assets - query := ` - SELECT - a.id AS asset_id, a.name AS asset_name, a.asset_type, a.code AS code, - COALESCE(a.image, '') AS image, a.contract_id, - d.id AS distributor_id, d.type AS distributor_type, d.funded AS distributor_funded, - dk.id AS distributor_key_id, dk.public_key AS distributor_key_public_key, dk.weight AS distributor_key_weight, - i.id AS issuer_id, i.type AS issuer_type, i.funded AS issuer_funded, - ik.id AS issuer_key_id, ik.public_key AS issuer_key_public_key, ik.weight AS issuer_key_weight - FROM asset a - JOIN wallet d ON a.distributor_id = d.id - JOIN key dk ON d.id = dk.wallet_id - JOIN wallet i ON a.issuer_id = i.id - JOIN key ik ON i.id = ik.wallet_id - ORDER BY a.name - LIMIT $1 OFFSET $2; + if len(conditions) > 0 { + queryBuilder += " WHERE " + strings.Join(conditions, " AND ") + } - ` + // Add ordering and pagination + queryBuilder += " ORDER BY a.name LIMIT $" + strconv.Itoa(len(args)+1) + " OFFSET $" + strconv.Itoa(len(args)+2) + args = append(args, limit, offset) - rows, err := r.Db.Query(query, limit, offset) + // Execute the query + rows, err := r.Db.Query(queryBuilder, args...) if err != nil { return nil, 0, fmt.Errorf("AssetRepo - GetPaginated - Query: %w", err) } @@ -247,6 +299,7 @@ func (r AssetRepo) GetPaginatedAssets(page int, limit int) ([]entity.Asset, int, err := rows.Scan( &asset.Id, &asset.Name, &asset.AssetType, &asset.Code, &image, &asset.ContractId, + &asset.AuthorizeRequired, &asset.ClawbackEnabled, &asset.FreezeEnabled, &distributor.Id, &distributor.Type, &distributor.Funded, &distributor.Key.Id, &distributor.Key.PublicKey, &distributor.Key.Weight, &issuer.Id, &issuer.Type, &issuer.Funded, @@ -256,16 +309,42 @@ func (r AssetRepo) GetPaginatedAssets(page int, limit int) ([]entity.Asset, int, return nil, 0, fmt.Errorf("AssetRepo - GetPaginated - row.Scan: %w", err) } - if !image.Valid { - asset.Image = "" - } else { + if image.Valid { asset.Image = image.String + } else { + asset.Image = "" } + asset.Distributor = distributor asset.Issuer = issuer assets = append(assets, asset) } + + // Check for errors after iterating through the rows + if err = rows.Err(); err != nil { + return nil, 0, fmt.Errorf("AssetRepo - GetPaginated - rows.Err: %w", err) + } + + countArgs := make([]interface{}, len(args)-2) + copy(countArgs, args[:len(args)-2]) + + // Building count query + countQueryBuilder := "SELECT COUNT(*) FROM asset a " + if len(conditions) > 0 { + countQueryBuilder += " WHERE " + strings.Join(conditions, " AND ") + } + + // Execute the count query + var totalAssets int + err = r.Db.QueryRow(countQueryBuilder, countArgs...).Scan(&totalAssets) + if err != nil { + return nil, 0, fmt.Errorf("AssetRepo - GetPaginated - Count Query: %w", err) + } + + // Calculate total pages + totalPages := (totalAssets + limit - 1) / limit + return assets, totalPages, nil } @@ -326,8 +405,12 @@ func (r AssetRepo) UpdateContractId(assetId string, contractId string) error { func (r AssetRepo) CreateAsset(data entity.Asset) (entity.Asset, error) { res := data - stmt := `INSERT INTO Asset (code, issuer_id, distributor_id, name, asset_type, image ) VALUES ($1, $2, $3,$4, $5, $6) RETURNING id;` - err := r.Db.QueryRow(stmt, data.Code, data.Issuer.Id, data.Distributor.Id, data.Name, data.AssetType, data.Image).Scan(&res.Id) + stmt := ` + INSERT INTO Asset (code, issuer_id, distributor_id, name, asset_type, image, authorize_required, clawback_enabled, freeze_enabled) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + RETURNING id; + ` + err := r.Db.QueryRow(stmt, data.Code, data.Issuer.Id, data.Distributor.Id, data.Name, data.AssetType, data.Image, data.AuthorizeRequired, data.ClawbackEnabled, data.FreezeEnabled).Scan(&res.Id) if err != nil { return entity.Asset{}, fmt.Errorf("AssetRepo - CreateAsset - db.QueryRow: %w", err) } diff --git a/backend/migrations/000040_alter_asset.down.sql b/backend/migrations/000040_alter_asset.down.sql new file mode 100644 index 00000000..4f1843f4 --- /dev/null +++ b/backend/migrations/000040_alter_asset.down.sql @@ -0,0 +1,4 @@ +ALTER TABLE Asset +DROP COLUMN IF EXISTS authorize_required, +DROP COLUMN IF EXISTS clawback_enabled, +DROP COLUMN IF EXISTS freeze_enabled; diff --git a/backend/migrations/000040_alter_asset.up.sql b/backend/migrations/000040_alter_asset.up.sql new file mode 100644 index 00000000..7ab655bf --- /dev/null +++ b/backend/migrations/000040_alter_asset.up.sql @@ -0,0 +1,5 @@ +ALTER TABLE Asset +ADD COLUMN authorize_required BOOLEAN DEFAULT false, +ADD COLUMN clawback_enabled BOOLEAN DEFAULT false, +ADD COLUMN freeze_enabled BOOLEAN DEFAULT false; + diff --git a/frontend/package-lock.json b/frontend/package-lock.json index fc24f16b..9d3d9069 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -39079,8 +39079,7 @@ "@chakra-ui/control-box": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/@chakra-ui/control-box/-/control-box-2.0.13.tgz", - "integrity": "sha512-FEyrU4crxati80KUF/+1Z1CU3eZK6Sa0Yv7Z/ydtz9/tvGblXW9NFanoomXAOvcIFLbaLQPPATm9Gmpr7VG05A==", - "requires": {} + "integrity": "sha512-FEyrU4crxati80KUF/+1Z1CU3eZK6Sa0Yv7Z/ydtz9/tvGblXW9NFanoomXAOvcIFLbaLQPPATm9Gmpr7VG05A==" }, "@chakra-ui/counter": { "version": "2.0.14", @@ -39095,8 +39094,7 @@ "@chakra-ui/css-reset": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-2.1.2.tgz", - "integrity": "sha512-4ySTLd+3iRpp4lX0yI9Yo2uQm2f+qwYGNOZF0cNcfN+4UJCd3IsaWxYRR/Anz+M51NVldZbYzC+TEYC/kpJc4A==", - "requires": {} + "integrity": "sha512-4ySTLd+3iRpp4lX0yI9Yo2uQm2f+qwYGNOZF0cNcfN+4UJCd3IsaWxYRR/Anz+M51NVldZbYzC+TEYC/kpJc4A==" }, "@chakra-ui/descendant": { "version": "3.0.14", @@ -39215,8 +39213,7 @@ "@chakra-ui/live-region": { "version": "2.0.13", "resolved": "https://registry.npmjs.org/@chakra-ui/live-region/-/live-region-2.0.13.tgz", - "integrity": "sha512-Ja+Slk6ZkxSA5oJzU2VuGU7TpZpbMb/4P4OUhIf2D30ctmIeXkxTWw1Bs1nGJAVtAPcGS5sKA+zb89i8g+0cTQ==", - "requires": {} + "integrity": "sha512-Ja+Slk6ZkxSA5oJzU2VuGU7TpZpbMb/4P4OUhIf2D30ctmIeXkxTWw1Bs1nGJAVtAPcGS5sKA+zb89i8g+0cTQ==" }, "@chakra-ui/media-query": { "version": "3.2.12", @@ -39441,14 +39438,12 @@ "@chakra-ui/react-children-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@chakra-ui/react-children-utils/-/react-children-utils-2.0.6.tgz", - "integrity": "sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==", - "requires": {} + "integrity": "sha512-QVR2RC7QsOsbWwEnq9YduhpqSFnZGvjjGREV8ygKi8ADhXh93C8azLECCUVgRJF2Wc+So1fgxmjLcbZfY2VmBA==" }, "@chakra-ui/react-context": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@chakra-ui/react-context/-/react-context-2.0.8.tgz", - "integrity": "sha512-tRTKdn6lCTXM6WPjSokAAKCw2ioih7Eg8cNgaYRSwKBck8nkz9YqxgIIEj3dJD7MGtpl24S/SNI98iRWkRwR/A==", - "requires": {} + "integrity": "sha512-tRTKdn6lCTXM6WPjSokAAKCw2ioih7Eg8cNgaYRSwKBck8nkz9YqxgIIEj3dJD7MGtpl24S/SNI98iRWkRwR/A==" }, "@chakra-ui/react-env": { "version": "3.0.0", @@ -39461,8 +39456,7 @@ "@chakra-ui/react-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@chakra-ui/react-types/-/react-types-2.0.7.tgz", - "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==", - "requires": {} + "integrity": "sha512-12zv2qIZ8EHwiytggtGvo4iLT0APris7T0qaAWqzpUGS0cdUtR8W+V1BJ5Ocq+7tA6dzQ/7+w5hmXih61TuhWQ==" }, "@chakra-ui/react-use-animation-state": { "version": "2.0.9", @@ -39476,8 +39470,7 @@ "@chakra-ui/react-use-callback-ref": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-callback-ref/-/react-use-callback-ref-2.0.7.tgz", - "integrity": "sha512-YjT76nTpfHAK5NxplAlZsQwNju5KmQExnqsWNPFeOR6vvbC34+iPSTr+r91i1Hdy7gBSbevsOsd5Wm6RN3GuMw==", - "requires": {} + "integrity": "sha512-YjT76nTpfHAK5NxplAlZsQwNju5KmQExnqsWNPFeOR6vvbC34+iPSTr+r91i1Hdy7gBSbevsOsd5Wm6RN3GuMw==" }, "@chakra-ui/react-use-controllable-state": { "version": "2.0.8", @@ -39533,14 +39526,12 @@ "@chakra-ui/react-use-latest-ref": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-latest-ref/-/react-use-latest-ref-2.0.5.tgz", - "integrity": "sha512-3mIuFzMyIo3Ok/D8uhV9voVg7KkrYVO/pwVvNPJOHsDQqCA6DpYE4WDsrIx+fVcwad3Ta7SupexR5PoI+kq6QQ==", - "requires": {} + "integrity": "sha512-3mIuFzMyIo3Ok/D8uhV9voVg7KkrYVO/pwVvNPJOHsDQqCA6DpYE4WDsrIx+fVcwad3Ta7SupexR5PoI+kq6QQ==" }, "@chakra-ui/react-use-merge-refs": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-merge-refs/-/react-use-merge-refs-2.0.7.tgz", - "integrity": "sha512-zds4Uhsc+AMzdH8JDDkLVet9baUBgtOjPbhC5r3A0ZXjZvGhCztFAVE3aExYiVoMPoHLKbLcqvCWE6ioFKz1lw==", - "requires": {} + "integrity": "sha512-zds4Uhsc+AMzdH8JDDkLVet9baUBgtOjPbhC5r3A0ZXjZvGhCztFAVE3aExYiVoMPoHLKbLcqvCWE6ioFKz1lw==" }, "@chakra-ui/react-use-outside-click": { "version": "2.1.0", @@ -39563,14 +39554,12 @@ "@chakra-ui/react-use-previous": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-previous/-/react-use-previous-2.0.5.tgz", - "integrity": "sha512-BIZgjycPE4Xr+MkhKe0h67uHXzQQkBX/u5rYPd65iMGdX1bCkbE0oorZNfOHLKdTmnEb4oVsNvfN6Rfr+Mnbxw==", - "requires": {} + "integrity": "sha512-BIZgjycPE4Xr+MkhKe0h67uHXzQQkBX/u5rYPd65iMGdX1bCkbE0oorZNfOHLKdTmnEb4oVsNvfN6Rfr+Mnbxw==" }, "@chakra-ui/react-use-safe-layout-effect": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-safe-layout-effect/-/react-use-safe-layout-effect-2.0.5.tgz", - "integrity": "sha512-MwAQBz3VxoeFLaesaSEN87reVNVbjcQBDex2WGexAg6hUB6n4gc1OWYH/iXp4tzp4kuggBNhEHkk9BMYXWfhJQ==", - "requires": {} + "integrity": "sha512-MwAQBz3VxoeFLaesaSEN87reVNVbjcQBDex2WGexAg6hUB6n4gc1OWYH/iXp4tzp4kuggBNhEHkk9BMYXWfhJQ==" }, "@chakra-ui/react-use-size": { "version": "2.0.10", @@ -39591,8 +39580,7 @@ "@chakra-ui/react-use-update-effect": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@chakra-ui/react-use-update-effect/-/react-use-update-effect-2.0.7.tgz", - "integrity": "sha512-vBM2bmmM83ZdDtasWv3PXPznpTUd+FvqBC8J8rxoRmvdMEfrxTiQRBJhiGHLpS9BPLLPQlosN6KdFU97csB6zg==", - "requires": {} + "integrity": "sha512-vBM2bmmM83ZdDtasWv3PXPznpTUd+FvqBC8J8rxoRmvdMEfrxTiQRBJhiGHLpS9BPLLPQlosN6KdFU97csB6zg==" }, "@chakra-ui/react-utils": { "version": "2.0.12", @@ -39831,8 +39819,7 @@ "@chakra-ui/visually-hidden": { "version": "2.0.15", "resolved": "https://registry.npmjs.org/@chakra-ui/visually-hidden/-/visually-hidden-2.0.15.tgz", - "integrity": "sha512-WWULIiucYRBIewHKFA7BssQ2ABLHLVd9lrUo3N3SZgR0u4ZRDDVEUNOy+r+9ruDze8+36dGbN9wsN1IdELtdOw==", - "requires": {} + "integrity": "sha512-WWULIiucYRBIewHKFA7BssQ2ABLHLVd9lrUo3N3SZgR0u4ZRDDVEUNOy+r+9ruDze8+36dGbN9wsN1IdELtdOw==" }, "@choc-ui/chakra-autocomplete": { "version": "5.1.9", @@ -40113,14 +40100,12 @@ "@csstools/postcss-unset-value": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", - "requires": {} + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==" }, "@csstools/selector-specificity": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", - "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", - "requires": {} + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==" }, "@discoveryjs/json-ext": { "version": "0.5.7", @@ -40240,8 +40225,7 @@ "@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", - "requires": {} + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==" }, "@emotion/utils": { "version": "1.2.1", @@ -40501,8 +40485,7 @@ "@hookform/resolvers": { "version": "2.9.11", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.9.11.tgz", - "integrity": "sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==", - "requires": {} + "integrity": "sha512-bA3aZ79UgcHj7tFV7RlgThzwSSHZgvfbt2wprldRkYBcMopdMvHyO17Wwp/twcJasNFischFfS7oz8Katz8DdQ==" }, "@humanwhocodes/config-array": { "version": "0.11.13", @@ -41474,8 +41457,7 @@ "version": "1.6.22", "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-1.6.22.tgz", "integrity": "sha512-TDoPum4SHdfPiGSAaRBw7ECyI8VaHpK8GJugbJIJuqyh6kzw9ZLJZW3HGL3NNrJGxcAixUvqROm+YuQOo5eXtg==", - "dev": true, - "requires": {} + "dev": true }, "@mdx-js/util": { "version": "1.6.22", @@ -41742,8 +41724,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "find-up": { "version": "5.0.0", @@ -42506,8 +42487,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "ansi-styles": { "version": "3.2.1", @@ -42885,8 +42865,7 @@ "webpack-filter-warnings-plugin": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/webpack-filter-warnings-plugin/-/webpack-filter-warnings-plugin-1.2.1.tgz", - "integrity": "sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==", - "requires": {} + "integrity": "sha512-Ez6ytc9IseDMLPo0qCuNNYzgtUl8NovOqjIq4uAU8LTD4uoa1w1KpZyyzFtLTEMZpkkOkLfL9eN+KGYdk1Qtwg==" } } }, @@ -43227,8 +43206,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "babel-plugin-polyfill-corejs3": { "version": "0.1.7", @@ -43767,8 +43745,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "braces": { "version": "2.3.2", @@ -44282,8 +44259,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "braces": { "version": "2.3.2", @@ -45199,8 +45175,7 @@ "version": "14.4.3", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.4.3.tgz", "integrity": "sha512-kCUc5MEwaEMakkO5x7aoD+DLi02ehmEM2QCGWvNqAS1dV/fAvORWEjnjsEIvml59M7Y5kCkWN6fCCyPOe8OL6Q==", - "dev": true, - "requires": {} + "dev": true }, "@tootallnate/once": { "version": "1.1.2", @@ -46495,14 +46470,12 @@ "acorn-import-assertions": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "requires": {} + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==" }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" }, "acorn-walk": { "version": "7.2.0", @@ -46578,8 +46551,7 @@ "ajv-errors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "requires": {} + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" }, "ajv-formats": { "version": "2.1.1", @@ -47312,8 +47284,7 @@ "babel-plugin-named-asset-import": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", - "requires": {} + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==" }, "babel-plugin-polyfill-corejs2": { "version": "0.4.6", @@ -49168,8 +49139,7 @@ "css-declaration-sorter": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.1.tgz", - "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==", - "requires": {} + "integrity": "sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==" }, "css-has-pseudo": { "version": "3.0.4", @@ -49293,8 +49263,7 @@ "css-prefers-color-scheme": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", - "requires": {} + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==" }, "css-select": { "version": "2.1.0", @@ -49396,8 +49365,7 @@ "cssnano-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "requires": {} + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" }, "csso": { "version": "4.2.0", @@ -51126,8 +51094,7 @@ "eslint-plugin-react-hooks": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", - "requires": {} + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==" }, "eslint-plugin-testing-library": { "version": "5.11.0", @@ -51726,8 +51693,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "json-schema-traverse": { "version": "0.4.1", @@ -52008,8 +51974,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "cosmiconfig": { "version": "6.0.0", @@ -54684,8 +54649,7 @@ "jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "requires": {} + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==" }, "jest-regex-util": { "version": "26.0.0", @@ -55897,8 +55861,7 @@ "ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", - "requires": {} + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" } } }, @@ -58239,8 +58202,7 @@ "postcss-browser-comments": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", - "requires": {} + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==" }, "postcss-calc": { "version": "8.2.4", @@ -58338,26 +58300,22 @@ "postcss-discard-comments": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "requires": {} + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "requires": {} + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "requires": {} + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "requires": {} + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" }, "postcss-double-position-gradients": { "version": "3.1.2", @@ -58414,14 +58372,12 @@ "postcss-font-variant": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", - "requires": {} + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==" }, "postcss-gap-properties": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", - "requires": {} + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==" }, "postcss-image-set-function": { "version": "4.0.7", @@ -58444,8 +58400,7 @@ "postcss-initial": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", - "requires": {} + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==" }, "postcss-js": { "version": "4.0.1", @@ -58506,8 +58461,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "json-schema-traverse": { "version": "0.4.1", @@ -58550,14 +58504,12 @@ "postcss-logical": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", - "requires": {} + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==" }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", - "requires": {} + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==" }, "postcss-merge-longhand": { "version": "5.1.7", @@ -58726,8 +58678,7 @@ "postcss-normalize-charset": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "requires": {} + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -58798,8 +58749,7 @@ "postcss-opacity-percentage": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", - "requires": {} + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==" }, "postcss-ordered-values": { "version": "5.1.3", @@ -58821,8 +58771,7 @@ "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", - "requires": {} + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==" }, "postcss-place": { "version": "7.0.5", @@ -58936,8 +58885,7 @@ "postcss-replace-overflow-wrap": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", - "requires": {} + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==" }, "postcss-selector-not": { "version": "6.0.1", @@ -59420,8 +59368,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "json-schema-traverse": { "version": "0.4.1", @@ -59472,8 +59419,7 @@ "react-chartjs-2": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", - "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", - "requires": {} + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==" }, "react-clientside-effect": { "version": "1.2.6", @@ -59630,8 +59576,7 @@ "react-docgen-typescript": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/react-docgen-typescript/-/react-docgen-typescript-2.2.2.tgz", - "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==", - "requires": {} + "integrity": "sha512-tvg2ZtOpOi6QDwsb3GZhOjDkkX0h8Z2gipvTg6OVMUyoYoURhEiRNePT8NZItTVCDh39JJHnLdfCOkzoLbFnTg==" }, "react-dom": { "version": "18.2.0", @@ -59724,8 +59669,7 @@ "react-hook-form": { "version": "7.43.9", "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.43.9.tgz", - "integrity": "sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==", - "requires": {} + "integrity": "sha512-AUDN3Pz2NSeoxQ7Hs6OhQhDr6gtF9YRuutGDwPQqhSUAHJSgGl2VeY3qN19MG0SucpjgDiuMJ4iC5T5uB+eaNQ==" }, "react-inspector": { "version": "5.1.1", @@ -59793,8 +59737,7 @@ "react-nanny": { "version": "2.15.0", "resolved": "https://registry.npmjs.org/react-nanny/-/react-nanny-2.15.0.tgz", - "integrity": "sha512-fn6tAnJ+UEdD0pq5YytlZJb5XmjVcvXoxq3i2r1o/BavgipwRWsw7oOXNJ8bJd33iedlkNyAQMXVC6qTl0Hv4A==", - "requires": {} + "integrity": "sha512-fn6tAnJ+UEdD0pq5YytlZJb5XmjVcvXoxq3i2r1o/BavgipwRWsw7oOXNJ8bJd33iedlkNyAQMXVC6qTl0Hv4A==" }, "react-number-format": { "version": "5.3.1", @@ -59959,8 +59902,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "clean-css": { "version": "5.3.2", @@ -60081,8 +60023,7 @@ "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "requires": {} + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" }, "jest-worker": { "version": "27.5.1", @@ -60118,8 +60059,7 @@ "postcss-flexbugs-fixes": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", - "requires": {} + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==" }, "postcss-loader": { "version": "6.2.1", @@ -60134,8 +60074,7 @@ "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "requires": {} + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" }, "postcss-modules-local-by-default": { "version": "4.0.3", @@ -60205,8 +60144,7 @@ "style-loader": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz", - "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==", - "requires": {} + "integrity": "sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw==" }, "supports-color": { "version": "8.1.1", @@ -60337,8 +60275,7 @@ "react-timeago": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/react-timeago/-/react-timeago-7.1.0.tgz", - "integrity": "sha512-rouF7MiEm55fH791Y8cg+VobIJgx8gtNJ+gjr86R4ZqO1WKPkXiXjdT/lRzrvEkUzsxT1exHqV2V+Zdi114H3A==", - "requires": {} + "integrity": "sha512-rouF7MiEm55fH791Y8cg+VobIJgx8gtNJ+gjr86R4ZqO1WKPkXiXjdT/lRzrvEkUzsxT1exHqV2V+Zdi114H3A==" }, "react-transition-group": { "version": "4.4.2", @@ -61371,8 +61308,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "json-schema-traverse": { "version": "0.4.1", @@ -62737,8 +62673,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "commander": { "version": "2.20.3", @@ -63550,8 +63485,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "json-schema-traverse": { "version": "0.4.1", @@ -63602,8 +63536,7 @@ "use-isomorphic-layout-effect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", - "requires": {} + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" }, "use-sidecar": { "version": "1.1.2", @@ -64094,8 +64027,7 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "requires": {} + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" }, "commander": { "version": "2.20.3", @@ -64738,8 +64670,7 @@ "ws": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "requires": {} + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==" }, "x-default-browser": { "version": "0.4.0", diff --git a/frontend/src/app/core/pages/authorize-account/index.tsx b/frontend/src/app/core/pages/authorize-account/index.tsx index 8d315702..eb28adb5 100644 --- a/frontend/src/app/core/pages/authorize-account/index.tsx +++ b/frontend/src/app/core/pages/authorize-account/index.tsx @@ -5,7 +5,13 @@ import { useToast, VStack, } from '@chakra-ui/react' -import React, { useCallback, useEffect, useState } from 'react' +import React, { + Dispatch, + SetStateAction, + useCallback, + useEffect, + useState, +} from 'react' import { FieldValues, UseFormSetValue } from 'react-hook-form' import { useNavigate, useParams } from 'react-router-dom' @@ -54,7 +60,8 @@ export const AuthorizeAccount: React.FC = () => { const onSubmit = async ( data: FieldValues, setValue: UseFormSetValue, - wallet: string | undefined + wallet: string | undefined, + setWallet: Dispatch> ): Promise => { if (!asset || !id) return @@ -68,6 +75,7 @@ export const AuthorizeAccount: React.FC = () => { if (isSuccess) { setValue('wallet', '') + setWallet(undefined) toast({ title: 'Authorize success!', description: `You authorized the account`, diff --git a/frontend/src/components/molecules/select-asset/index.tsx b/frontend/src/components/molecules/select-asset/index.tsx index 221f8bd8..8ba6cf2c 100644 --- a/frontend/src/components/molecules/select-asset/index.tsx +++ b/frontend/src/components/molecules/select-asset/index.tsx @@ -18,6 +18,7 @@ interface ISelectAsset { selected?: IOption | null setAsset: Dispatch> setSelected?: Dispatch> + clearErrors?(): void } const createOption = ( @@ -33,6 +34,7 @@ export const SelectAsset: React.FC = ({ selected, setAsset, setSelected, + clearErrors, }) => { const { colorMode } = useColorMode() const [options, setOptions] = useState([]) @@ -46,7 +48,9 @@ export const SelectAsset: React.FC = ({ const formatLabel = (data: IOption): ReactNode => ( - {data.label} + + {data.label} + {data.value.name} ) @@ -55,6 +59,7 @@ export const SelectAsset: React.FC = ({ setWallet(newValue?.value)} + onChange={(newValue): void => { + if (clearErrors) clearErrors() + setWallet(newValue?.value) + }} isOptionDisabled={(option): boolean => option.disabled} formatOptionLabel={formatLabel} + noOptionsMessage={({ inputValue }): string => + !inputValue ? noOptionsMessage : 'No results found' + } styles={{ control: baseStyles => ({ ...baseStyles, @@ -95,7 +105,6 @@ export const SelectVault: React.FC = ({ }), option: (styles, { isFocused, isSelected, isDisabled }) => ({ ...styles, - background: isFocused ? colorMode === 'dark' ? '#292d3e' diff --git a/frontend/src/components/templates/authorize-account/index.tsx b/frontend/src/components/templates/authorize-account/index.tsx index a04dee0f..47fe5196 100644 --- a/frontend/src/components/templates/authorize-account/index.tsx +++ b/frontend/src/components/templates/authorize-account/index.tsx @@ -11,7 +11,7 @@ import { RadioGroup, Stack, } from '@chakra-ui/react' -import React, { useState } from 'react' +import React, { Dispatch, SetStateAction, useState } from 'react' import { FieldValues, UseFormSetValue, useForm } from 'react-hook-form' import { AssetHeader } from 'components/atoms' @@ -23,7 +23,8 @@ interface IAuthorizeAccountTemplate { onSubmit( data: FieldValues, setValue: UseFormSetValue, - wallet: string | undefined + wallet: string | undefined, + setWallet: Dispatch> ): Promise loading: boolean asset: Hooks.UseAssetsTypes.IAssetDto @@ -43,6 +44,8 @@ export const AuthorizeAccountTemplate: React.FC = ({ formState: { errors }, handleSubmit, setValue, + clearErrors, + setError, } = useForm() const [wallet, setWallet] = useState() @@ -50,6 +53,17 @@ export const AuthorizeAccountTemplate: React.FC = ({ 'INTERNAL' ) + const handleForm = (data: FieldValues): void => { + if ( + (typeAccount === 'INTERNAL' && !wallet) || + (typeAccount === 'EXTERNAL' && !data.wallet) + ) { + setError('wallet', { message: 'This field is required' }) + return + } + onSubmit(data, setValue, wallet, setWallet) + } + return ( @@ -68,22 +82,24 @@ export const AuthorizeAccountTemplate: React.FC = ({ -
{ - onSubmit(data, setValue, wallet) - })} - > + handleForm(data))}> {typeAccount === 'INTERNAL' ? ( - Vault + Vault or wallet { + clearErrors('wallet') + }} + noOptionsMessage="No vaults or wallets with pending authorization" /> - Required + + {errors?.wallet?.message?.toString()} + ) : ( - + Wallet = ({ required: true, })} /> - Required + + {errors?.wallet?.message?.toString()} + )} diff --git a/frontend/src/components/templates/burn-asset/index.tsx b/frontend/src/components/templates/burn-asset/index.tsx index 9d1e1378..06b31bab 100644 --- a/frontend/src/components/templates/burn-asset/index.tsx +++ b/frontend/src/components/templates/burn-asset/index.tsx @@ -53,18 +53,24 @@ export const BurnAssetTemplate: React.FC = ({ handleSubmit, setValue, getValues, + setError, + clearErrors, } = useForm() + const handleForm = (data: FieldValues): void => { + if (!data.amount) { + setError('amount', { message: 'This field is required' }) + return + } + onSubmit(data, setValue) + } + return ( - { - onSubmit(data, setValue) - })} - > + handleForm(data))}> Amount to burn @@ -80,10 +86,13 @@ export const BurnAssetTemplate: React.FC = ({ autoComplete="off" value={getValues('amount')} onChange={(event): void => { + clearErrors('amount') setValue('amount', toNumber(event.target.value)) }} /> - Required + + {errors?.amount?.message?.toString()} + = ({ handleSubmit, setValue, getValues, + clearErrors, + setError, } = useForm() const [wallet, setWallet] = useState() @@ -53,6 +55,27 @@ export const ClawbackAssetTemplate: React.FC = ({ 'INTERNAL' ) + const handleForm = (data: FieldValues): void => { + clearErrors() + let hasError = false + if (!data.amount) { + setError('amount', { message: 'This field is required' }) + hasError = true + } + if (typeAccount === 'INTERNAL' && !wallet) { + setError('wallet', { message: 'This field is required' }) + hasError = true + } + + if (typeAccount === 'EXTERNAL' && !data.from) { + setError('from', { message: 'This field is required' }) + hasError = true + } + if (!hasError) { + onSubmit(data, setValue, wallet) + } + } + return ( @@ -71,20 +94,25 @@ export const ClawbackAssetTemplate: React.FC = ({ - { - onSubmit(data, setValue, wallet) - })} - > + handleForm(data))}> {typeAccount === 'INTERNAL' ? ( Vault - - Required + { + clearErrors('wallet') + }} + noOptionsMessage="No vaults or wallets with funds" + /> + + {errors?.wallet?.message?.toString()} + ) : ( Wallet = ({ required: true, })} /> - Required + + {errors?.from?.message?.toString()} + )} @@ -108,10 +138,13 @@ export const ClawbackAssetTemplate: React.FC = ({ autoComplete="off" value={getValues('amount')} onChange={(event): void => { + clearErrors('amount') setValue('amount', toNumber(event.target.value)) }} /> - Required + + {errors?.amount?.message?.toString()} + > distributorWallet?: string + clearErrors?(): void } const createOption = ( @@ -28,6 +29,7 @@ export const SelectVault: React.FC = ({ vaults, setVault, distributorWallet, + clearErrors, }) => { const { colorMode } = useColorMode() const [options, setOptions] = useState([]) @@ -45,7 +47,10 @@ export const SelectVault: React.FC = ({ return ( = ({ autoComplete="off" value={getValues('min_deposit')} onChange={(event): void => { + clearErrors('min_deposit') setValue('min_deposit', toNumber(event.target.value)) }} /> + + {errors?.min_deposit?.message?.toString()} + - - Term + + Term* = ({ autoComplete="off" value={getValues('term')} onChange={(event): void => { + clearErrors('term') setValue('term', toNumber(event.target.value)) }} /> + + {errors?.term?.message?.toString()} + @@ -187,8 +239,8 @@ export const ContractsCreateTemplate: React.FC = ({ flexDir={{ base: 'column', md: 'row' }} gap="1.5rem" > - - Yield Rate + + Yield Rate* = ({ autoComplete="off" placeholder="Yield Rate" onChange={(event): void => { + clearErrors('yield_rate') setValue('yield_rate', toNumber(event.target.value)) }} /> + + {errors?.yield_rate?.message?.toString()} + - - Penalty rate + + Penalty rate* = ({ autoComplete="off" value={getValues('penalty_rate')} onChange={(event): void => { + clearErrors('penalty_rate') setValue( 'penalty_rate', toNumber(event.target.value) @@ -222,6 +279,9 @@ export const ContractsCreateTemplate: React.FC = ({ /> + + {errors?.penalty_rate?.message?.toString()} + @@ -236,7 +296,7 @@ export const ContractsCreateTemplate: React.FC = ({ _dark={{ bg: 'black.600' }} > - Compound + Compound* = ({ formState: { errors }, handleSubmit, setValue, + setError, + clearErrors, getValues, } = useForm() const [wallet, setWallet] = useState() + const handleForm = (data: FieldValues): void => { + clearErrors() + let hasError = false + if (!data.amount) { + setError('amount', { message: 'This field is required' }) + hasError = true + } + if (!wallet) { + setError('wallet', { message: 'This field is required' }) + hasError = true + } + if (!hasError) { + onSubmit(data, setValue, wallet) + } + } + return ( - { - onSubmit(data, setValue, wallet) - })} - > + handleForm(data))}> - + Destination - - Required + { + clearErrors('wallet') + }} + /> + + {errors?.wallet?.message?.toString()} + @@ -82,10 +102,13 @@ export const DistributeAssetTemplate: React.FC = ({ autoComplete="off" value={getValues('amount')} onChange={(event): void => { + clearErrors('amount') setValue('amount', toNumber(event.target.value)) }} /> - Required + + {errors?.amount?.message?.toString()} + = ({ })} > - + Asset Icon {selectedFile && ( @@ -194,7 +194,7 @@ export const ForgeAssetTemplate: React.FC = ({ className="asset-name" /> - Name must be more than 2 characters + Asset name must be between 2 and 48 characters long @@ -217,7 +217,7 @@ export const ForgeAssetTemplate: React.FC = ({ className="asset-code" /> - Code must be 3 characters + Code must be between 3 and 12 characters long @@ -309,7 +309,7 @@ export const ForgeAssetTemplate: React.FC = ({ type="submit" variant="primary" mt="1.5rem" - w={{base: 'full', md: 'fit-content'}} + w={{ base: 'full', md: 'fit-content' }} isLoading={loading} > Forge asset diff --git a/frontend/src/components/templates/freeze-account/index.tsx b/frontend/src/components/templates/freeze-account/index.tsx index 61ca1ef6..12a4e694 100644 --- a/frontend/src/components/templates/freeze-account/index.tsx +++ b/frontend/src/components/templates/freeze-account/index.tsx @@ -44,6 +44,8 @@ export const FreezeAccountTemplate: React.FC = ({ formState: { errors }, handleSubmit, setValue, + clearErrors, + setError, } = useForm() const [loadingFreeze, setLoadingFreeze] = useState(false) @@ -55,12 +57,32 @@ export const FreezeAccountTemplate: React.FC = ({ ) const freeze = (data: FieldValues): void => { + if (typeAccount === 'INTERNAL' && !wallet) { + setError('wallet', { message: 'This field is required' }) + return + } + + if (typeAccount === 'EXTERNAL' && !data.trustor_pk) { + setError('trustor_pk', { message: 'This field is required' }) + return + } + setLoadingUnfreeze(false) setLoadingFreeze(true) onSubmit(data, ['TRUST_LINE_AUTHORIZED'], [], setValue, wallet) } const unfreeze = (data: FieldValues): void => { + if (typeAccount === 'INTERNAL' && !wallet) { + setError('wallet', { message: 'This field is required' }) + return + } + + if (typeAccount === 'EXTERNAL' && !data.trustor_pk) { + setError('trustor_pk', { message: 'This field is required' }) + return + } + setLoadingFreeze(false) setLoadingUnfreeze(true) onSubmit(data, [], ['TRUST_LINE_AUTHORIZED'], setValue, wallet) @@ -87,20 +109,31 @@ export const FreezeAccountTemplate: React.FC = ({ {typeAccount === 'INTERNAL' ? ( Vault - - Required + { + clearErrors('wallet') + }} + noOptionsMessage="No authorized vaults or wallets" + /> + + {errors?.wallet?.message?.toString()} + ) : ( - + Wallet - Required + + {errors?.trustor_pk?.message?.toString()} + )} diff --git a/frontend/src/components/templates/login/index.tsx b/frontend/src/components/templates/login/index.tsx index 72c53573..ae9c9f69 100644 --- a/frontend/src/components/templates/login/index.tsx +++ b/frontend/src/components/templates/login/index.tsx @@ -63,7 +63,7 @@ export const LoginTemplate: React.FC = ({ {error && ( - + {error} diff --git a/frontend/src/components/templates/mint-asset/index.tsx b/frontend/src/components/templates/mint-asset/index.tsx index b0f23028..a3cd9a5e 100644 --- a/frontend/src/components/templates/mint-asset/index.tsx +++ b/frontend/src/components/templates/mint-asset/index.tsx @@ -53,18 +53,24 @@ export const MintAssetTemplate: React.FC = ({ handleSubmit, setValue, getValues, + setError, + clearErrors, } = useForm() + const handleForm = (data: FieldValues): void => { + if (!data.amount) { + setError('amount', { message: 'This field is required' }) + return + } + onSubmit(data, setValue) + } + return ( - { - onSubmit(data, setValue) - })} - > + handleForm(data))}> Amount to mint @@ -80,10 +86,13 @@ export const MintAssetTemplate: React.FC = ({ autoComplete="off" value={getValues('amount')} onChange={(event): void => { + clearErrors('amount') setValue('amount', toNumber(event.target.value)) }} /> - Required + + {errors?.amount?.message?.toString()} + = ({ const onSubmit = async (data: FieldValues): Promise => { setErrorSubmit(null) + try { const isSuccess = await handleRole(data.name, role?.id) @@ -85,14 +86,14 @@ export const ModalRoleManage: React.FC = ({ onSubmit(data) })} > - + Role - Inform the role + This field is required - + + + +
diff --git a/frontend/src/components/templates/vault-create/select-category/index.tsx b/frontend/src/components/templates/vault-create/select-category/index.tsx index 1bc3ccb6..88fe83cc 100644 --- a/frontend/src/components/templates/vault-create/select-category/index.tsx +++ b/frontend/src/components/templates/vault-create/select-category/index.tsx @@ -11,6 +11,7 @@ interface ISelectCategory { ): Promise setCategorySelected: Dispatch> categorySelected?: IOption | null | undefined + clearErrors?(): void } const createOption = (label: string, value: number): IOption => ({ @@ -23,6 +24,7 @@ export const SelectCategory: React.FC = ({ createVaultCategory, setCategorySelected, categorySelected, + clearErrors }) => { const [isLoading, setIsLoading] = useState(false) const [options, setOptions] = useState([]) @@ -57,7 +59,10 @@ export const SelectCategory: React.FC = ({ isLoading={isLoading} onCreateOption={handleVaultCategory} options={options} - onChange={(newValue): void => setCategorySelected(newValue)} + onChange={(newValue): void => { + if (clearErrors) clearErrors() + setCategorySelected(newValue) + }} defaultValue={ categorySelected ? { value: categorySelected.value, label: categorySelected.label } diff --git a/frontend/src/config/theme/components/atoms/alert.ts b/frontend/src/config/theme/components/atoms/alert.ts index ff38596a..b0d2fc40 100644 --- a/frontend/src/config/theme/components/atoms/alert.ts +++ b/frontend/src/config/theme/components/atoms/alert.ts @@ -20,5 +20,14 @@ export const Alert = { color: 'purple.500', }, }, + error: { + container: { + bg: 'red.500', + color: 'white', + }, + icon: { + color: 'white', + }, + }, }, } diff --git a/frontend/src/hooks/useAssets/context.tsx b/frontend/src/hooks/useAssets/context.tsx index 87a26e16..92e6cad7 100644 --- a/frontend/src/hooks/useAssets/context.tsx +++ b/frontend/src/hooks/useAssets/context.tsx @@ -35,7 +35,7 @@ export const AssetsProvider: React.FC = ({ children }) => { return undefined } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(error.message) + throw new Error(`Forge error! ${error?.response?.data.message}`) } throw new Error(MessagesError.errorOccurred) } finally { @@ -52,7 +52,7 @@ export const AssetsProvider: React.FC = ({ children }) => { return response.status === 200 } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(error.message) + throw new Error(`Mint error! ${error?.response?.data.message}`) } throw new Error(MessagesError.errorOccurred) } finally { @@ -69,7 +69,7 @@ export const AssetsProvider: React.FC = ({ children }) => { return response.status === 200 } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(error.message) + throw new Error(`Burn error! ${error?.response?.data.message}`) } throw new Error(MessagesError.errorOccurred) } finally { @@ -86,7 +86,7 @@ export const AssetsProvider: React.FC = ({ children }) => { return response.status === 200 } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(error.message) + throw new Error(`Distribute error! ${error?.response?.data.message}`) } throw new Error(MessagesError.errorOccurred) } finally { @@ -103,7 +103,7 @@ export const AssetsProvider: React.FC = ({ children }) => { return response.status === 200 } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(error.message) + throw new Error(`Authorize error! ${error?.response?.data.message}`) } throw new Error(MessagesError.errorOccurred) } finally { @@ -120,7 +120,7 @@ export const AssetsProvider: React.FC = ({ children }) => { return response.status === 200 } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(error.message) + throw new Error(`Freeze error! ${error?.response?.data.message}`) } throw new Error(MessagesError.errorOccurred) } finally { @@ -137,7 +137,7 @@ export const AssetsProvider: React.FC = ({ children }) => { return response.status === 200 } catch (error) { if (axios.isAxiosError(error)) { - throw new Error(error.message) + throw new Error(`Clawback error! ${error?.response?.data.message}`) } throw new Error(MessagesError.errorOccurred) } finally { diff --git a/frontend/src/hooks/useAuth/context.tsx b/frontend/src/hooks/useAuth/context.tsx index 8cef3874..b842969f 100644 --- a/frontend/src/hooks/useAuth/context.tsx +++ b/frontend/src/hooks/useAuth/context.tsx @@ -60,9 +60,6 @@ export const AuthProvider: React.FC = ({ children }) => { return user } catch (error) { - if (axios.isAxiosError(error) && error?.response?.status === 400) { - throw new Error(error.message) - } throw new Error(MessagesError.invalidCredentials) } finally { setLoading(false) @@ -85,10 +82,20 @@ export const AuthProvider: React.FC = ({ children }) => { return user } catch (error) { - if (axios.isAxiosError(error) && error?.response?.status === 400) { - throw new Error(error.message) + if ( + axios.isAxiosError(error) && + error?.response?.data.error?.includes('useraccount_email_key') + ) { + throw new Error( + 'Failed to register. This email address is already in use.' + ) + } + if (axios.isAxiosError(error)) { + throw new Error( + `${MessagesError.signUpFailed} ${error?.response?.data.message}` + ) } - throw new Error(MessagesError.signUpFailed) + throw new Error(`${MessagesError.signUpFailed} ${error}`) } finally { setLoading(false) } diff --git a/frontend/src/utils/constants/messages-error.tsx b/frontend/src/utils/constants/messages-error.tsx index 5018f1e2..73701be3 100644 --- a/frontend/src/utils/constants/messages-error.tsx +++ b/frontend/src/utils/constants/messages-error.tsx @@ -1,5 +1,5 @@ export const MessagesError = { - invalidCredentials: 'Invalid credentials', + invalidCredentials: 'The email address or password is incorrect.', errorOccurred: 'Sorry, an error occurred, please try again.', - signUpFailed: 'Failed to register, please try again.', + signUpFailed: 'Failed to register.', }