Webhook responses
parent
cdca5a2553
commit
7585ad3450
|
@ -3,6 +3,8 @@
|
|||
## 1.5.0 Betula Pendula - FOSDEM 2015 (unreleased)
|
||||
|
||||
### Features
|
||||
- Taiga webhooks
|
||||
+ Created admin panel with webhook settings.
|
||||
- Not showing closed milestones by default in backlog view.
|
||||
- In kanban view an archived user story status doesn't show his content by default.
|
||||
- Now you can export and import projects between Taiga instances.
|
||||
|
|
|
@ -24,9 +24,239 @@ taiga = @.taiga
|
|||
mixOf = @.taiga.mixOf
|
||||
bindMethods = @.taiga.bindMethods
|
||||
debounce = @.taiga.debounce
|
||||
timeout = @.taiga.timeout
|
||||
|
||||
module = angular.module("taigaAdmin")
|
||||
|
||||
#############################################################################
|
||||
## Webhooks
|
||||
#############################################################################
|
||||
|
||||
class WebhooksController extends mixOf(taiga.Controller, taiga.PageMixin, taiga.FiltersMixin)
|
||||
@.$inject = [
|
||||
"$scope",
|
||||
"$tgRepo",
|
||||
"$tgResources",
|
||||
"$routeParams",
|
||||
"$appTitle"
|
||||
]
|
||||
|
||||
constructor: (@scope, @repo, @rs, @params, @appTitle) ->
|
||||
bindMethods(@)
|
||||
|
||||
@scope.sectionName = "Webhooks" #i18n
|
||||
@scope.project = {}
|
||||
|
||||
promise = @.loadInitialData()
|
||||
|
||||
promise.then () =>
|
||||
@appTitle.set("Webhooks - " + @scope.project.name)
|
||||
|
||||
promise.then null, @.onInitialDataError.bind(@)
|
||||
|
||||
@scope.$on "webhooks:reload", @.loadWebhooks
|
||||
|
||||
loadWebhooks: ->
|
||||
return @rs.webhooks.list(@scope.projectId).then (webhooks) =>
|
||||
@scope.webhooks = webhooks
|
||||
|
||||
loadProject: ->
|
||||
return @rs.projects.get(@scope.projectId).then (project) =>
|
||||
@scope.project = project
|
||||
@scope.$emit('project:loaded', project)
|
||||
return project
|
||||
|
||||
loadInitialData: ->
|
||||
promise = @repo.resolve({pslug: @params.pslug}).then (data) =>
|
||||
@scope.projectId = data.project
|
||||
return data
|
||||
|
||||
return promise.then(=> @.loadProject())
|
||||
.then(=> @.loadWebhooks())
|
||||
|
||||
module.controller("WebhooksController", WebhooksController)
|
||||
|
||||
#############################################################################
|
||||
## Webhook Directive
|
||||
#############################################################################
|
||||
|
||||
WebhookDirective = ($rs, $repo, $confirm, $loading) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
webhook = $scope.$eval($attrs.tgWebhook)
|
||||
|
||||
updateLogs = () ->
|
||||
$rs.webhooklogs.list(webhook.id).then (webhooklogs) =>
|
||||
webhooklogs = webhooklogs.reverse()
|
||||
for log in webhooklogs
|
||||
statusText = String(log.status)
|
||||
log.validStatus = statusText.length==3 and statusText[0]="2"
|
||||
log.prettySentData = JSON.stringify(log.request_data.data, undefined, 2)
|
||||
log.prettySentHeaders = JSON.stringify(log.request_headers, undefined, 2)
|
||||
log.prettyDate = moment(log.created).format("DD MMM YYYY [at] hh:mm:ss")
|
||||
|
||||
webhook.logs = webhooklogs
|
||||
updateShowHideHistoryText()
|
||||
|
||||
updateShowHideHistoryText = () ->
|
||||
textElement = $el.find(".toggle-history")
|
||||
historyElement = textElement.parents(".single-webhook-wrapper").find(".webhooks-history")
|
||||
if historyElement.hasClass("open")
|
||||
textElement.text("(Hide history)")
|
||||
else
|
||||
textElement.text("(Show history)")
|
||||
|
||||
showVisualizationMode = () ->
|
||||
$el.find(".edition-mode").addClass("hidden")
|
||||
$el.find(".visualization-mode").removeClass("hidden")
|
||||
|
||||
showEditMode = () ->
|
||||
$el.find(".visualization-mode").addClass("hidden")
|
||||
$el.find(".edition-mode").removeClass("hidden")
|
||||
|
||||
cancel = () ->
|
||||
showVisualizationMode()
|
||||
$scope.$apply ->
|
||||
webhook.revert()
|
||||
|
||||
save = debounce 2000, (target) ->
|
||||
form = target.parents("form").checksley()
|
||||
return if not form.validate()
|
||||
|
||||
value = target.scope().value
|
||||
promise = $repo.save(webhook)
|
||||
promise.then =>
|
||||
showVisualizationMode()
|
||||
|
||||
promise.then null, (data) ->
|
||||
$confirm.notify("error")
|
||||
form.setErrors(data)
|
||||
|
||||
$el.on "click", ".test-webhook", () ->
|
||||
$rs.webhooks.test(webhook.id).then =>
|
||||
updateLogs()
|
||||
$el.find(".webhooks-history").addClass("open")
|
||||
updateShowHideHistoryText()
|
||||
|
||||
$el.on "click", ".edit-webhook", () ->
|
||||
showEditMode()
|
||||
|
||||
$el.on "click", ".cancel-existing", () ->
|
||||
cancel()
|
||||
|
||||
$el.on "click", ".edit-existing", (event) ->
|
||||
event.preventDefault()
|
||||
target = angular.element(event.currentTarget)
|
||||
save(target)
|
||||
|
||||
$el.on "keyup", ".edition-mode input", (event) ->
|
||||
if event.keyCode == 13
|
||||
target = angular.element(event.currentTarget)
|
||||
saveWebhook(target)
|
||||
else if event.keyCode == 27
|
||||
target = angular.element(event.currentTarget)
|
||||
cancel(target)
|
||||
|
||||
$el.on "click", ".delete-webhook", () ->
|
||||
title = "Delete webhook" #TODO: i18in
|
||||
message = "Webhook '#{webhook.name}'" #TODO: i18in
|
||||
|
||||
$confirm.askOnDelete(title, message).then (finish) =>
|
||||
onSucces = ->
|
||||
finish()
|
||||
$scope.$emit("webhooks:reload")
|
||||
|
||||
onError = ->
|
||||
finish(false)
|
||||
$confirm.notify("error")
|
||||
|
||||
$repo.remove(webhook).then(onSucces, onError)
|
||||
|
||||
$el.on "click", ".toggle-history", (event) ->
|
||||
target = angular.element(event.currentTarget)
|
||||
if not webhook.logs? or webhook.logs.length == 0
|
||||
updateLogs().then ->
|
||||
#Waiting for ng-repeat to finish
|
||||
timeout 0, ->
|
||||
$el.find(".webhooks-history").toggleClass("open")
|
||||
updateShowHideHistoryText()
|
||||
|
||||
else
|
||||
$el.find(".webhooks-history").toggleClass("open")
|
||||
$scope.$apply () ->
|
||||
updateShowHideHistoryText()
|
||||
|
||||
|
||||
$el.on "click", ".history-single", (event) ->
|
||||
target = angular.element(event.currentTarget)
|
||||
target.toggleClass("history-single-open")
|
||||
target.siblings(".history-single-response").toggleClass("open")
|
||||
|
||||
$el.on "click", ".resend-request", (event) ->
|
||||
target = angular.element(event.currentTarget)
|
||||
log = target.data("log")
|
||||
$rs.webhooklogs.resend(log).then () =>
|
||||
updateLogs()
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgWebhook", ["$tgResources", "$tgRepo", "$tgConfirm", "$tgLoading", WebhookDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## New webhook Directive
|
||||
#############################################################################
|
||||
|
||||
NewWebhookDirective = ($rs, $repo, $confirm, $loading) ->
|
||||
link = ($scope, $el, $attrs) ->
|
||||
webhook = $scope.$eval($attrs.tgWebhook)
|
||||
formDOMNode = $el.find(".new-webhook-form")
|
||||
addWebhookDOMNode = $el.find(".add-webhook")
|
||||
initializeNewValue = ->
|
||||
$scope.newValue = {
|
||||
"name": ""
|
||||
"url": ""
|
||||
"key": ""
|
||||
}
|
||||
|
||||
initializeNewValue()
|
||||
|
||||
$scope.$watch "webhooks", (webhooks) ->
|
||||
if webhooks?
|
||||
if webhooks.length == 0
|
||||
formDOMNode.removeClass("hidden")
|
||||
addWebhookDOMNode.addClass("hidden")
|
||||
formDOMNode.find("input")[0].focus()
|
||||
else
|
||||
formDOMNode.addClass("hidden")
|
||||
addWebhookDOMNode.removeClass("hidden")
|
||||
|
||||
formDOMNode.on "click", ".add-new", debounce 2000, (event) ->
|
||||
event.preventDefault()
|
||||
form = formDOMNode.checksley()
|
||||
return if not form.validate()
|
||||
|
||||
$scope.newValue.project = $scope.project.id
|
||||
promise = $repo.create("webhooks", $scope.newValue)
|
||||
promise.then =>
|
||||
$scope.$emit("webhooks:reload")
|
||||
initializeNewValue()
|
||||
|
||||
promise.then null, (data) ->
|
||||
$confirm.notify("error")
|
||||
form.setErrors(data)
|
||||
|
||||
formDOMNode.on "click", ".cancel-new", (event) ->
|
||||
$scope.$apply ->
|
||||
initializeNewValue()
|
||||
|
||||
addWebhookDOMNode.on "click", (event) ->
|
||||
formDOMNode.removeClass("hidden")
|
||||
formDOMNode.find("input")[0].focus()
|
||||
|
||||
return {link:link}
|
||||
|
||||
module.directive("tgNewWebhook", ["$tgResources", "$tgRepo", "$tgConfirm", "$tgLoading", NewWebhookDirective])
|
||||
|
||||
|
||||
#############################################################################
|
||||
## Github Controller
|
||||
|
|
|
@ -85,6 +85,10 @@ urls = {
|
|||
"priorities": "/priorities"
|
||||
"severities": "/severities"
|
||||
"project-modules": "/projects/%s/modules"
|
||||
"webhooks": "/webhooks"
|
||||
"webhooks-test": "/webhooks/%s/test"
|
||||
"webhooklogs": "/webhooklogs"
|
||||
"webhooklogs-resend": "/webhooklogs/%s/resend"
|
||||
|
||||
# History
|
||||
"history/us": "/history/userstory"
|
||||
|
@ -145,5 +149,7 @@ module.run([
|
|||
"$tgHistoryResourcesProvider",
|
||||
"$tgKanbanResourcesProvider",
|
||||
"$tgModulesResourcesProvider",
|
||||
"$tgWebhooksResourcesProvider",
|
||||
"$tgWebhookLogsResourcesProvider",
|
||||
initResources
|
||||
])
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
resourceProvider = ($repo, $urls, $http) ->
|
||||
service = {}
|
||||
|
||||
service.list = (webhookId) ->
|
||||
params = {webhook: webhookId}
|
||||
return $repo.queryMany("webhooklogs", params)
|
||||
|
||||
service.resend = (webhooklogId) ->
|
||||
url = $urls.resolve("webhooklogs-resend", webhooklogId)
|
||||
return $http.post(url)
|
||||
|
||||
return (instance) ->
|
||||
instance.webhooklogs = service
|
||||
|
||||
|
||||
module = angular.module("taigaResources")
|
||||
module.factory("$tgWebhookLogsResourcesProvider", ["$tgRepo", "$tgUrls", "$tgHttp", resourceProvider])
|
|
@ -0,0 +1,17 @@
|
|||
resourceProvider = ($repo, $urls, $http) ->
|
||||
service = {}
|
||||
|
||||
service.list = (projectId) ->
|
||||
params = {project: projectId}
|
||||
return $repo.queryMany("webhooks", params)
|
||||
|
||||
service.test = (webhookId) ->
|
||||
url = $urls.resolve("webhooks-test", webhookId)
|
||||
return $http.post(url)
|
||||
|
||||
return (instance) ->
|
||||
instance.webhooks = service
|
||||
|
||||
|
||||
module = angular.module("taigaResources")
|
||||
module.factory("$tgWebhooksResourcesProvider", ["$tgRepo", "$tgUrls", "$tgHttp", resourceProvider])
|
|
@ -2,19 +2,19 @@ block head
|
|||
title Taiga Your agile, free, and open source project management tool
|
||||
|
||||
block content
|
||||
div.wrapper.roles(tg-github-webhooks, ng-controller="GithubController as ctrl",
|
||||
div.wrapper.roles(ng-controller="WebhooksController as ctrl",
|
||||
ng-init="section='admin'")
|
||||
sidebar.menu-secondary.sidebar(tg-admin-navigation="Webhooks")
|
||||
include ../admin-menu
|
||||
sidebar.menu-tertiary.sidebar(tg-admin-navigation="third-parties-webhooks")
|
||||
include ../admin-submenu-third-parties
|
||||
|
||||
section.main.admin-common.admin-webhooks
|
||||
section.main.admin-common.admin-webhooks(tg-new-webhook)
|
||||
include ../../components/mainTitle
|
||||
|
||||
p.admin-subtitle Webhooks notify external services about events in Taiga, like comments, user stories....
|
||||
div.webhooks-options
|
||||
a.button.button-green.add-webhook(href="",title="Add a New Webhook") Add Webhook
|
||||
a.button.button-green.hidden.add-webhook(href="",title="Add a New Webhook") Add Webhook
|
||||
|
||||
section.webhooks-table.basic-table
|
||||
div.table-header
|
||||
|
@ -23,72 +23,77 @@ block content
|
|||
div.webhook-url URL
|
||||
div.webhook-options
|
||||
div.table-body
|
||||
div.single-webhook-wrapper(tg-webhook="webhook", ng-repeat="webhook in webhooks")
|
||||
div.edition-mode.hidden
|
||||
form.row
|
||||
div.webhook-service
|
||||
input(type="text", name="service-name", placeholder="Type the service name")
|
||||
fieldset.webhook-service
|
||||
input(type="text", name="name", placeholder="Type the service name", data-required="true", ng-model="webhook.name")
|
||||
div.webhook-url
|
||||
div.webhook-url-inputs
|
||||
input(type="text", name="service-sexret-key", placeholder="Type the service secret key")
|
||||
input(type="text", name="service-payload-url", placeholder="Type the service payload url")
|
||||
fieldset
|
||||
input(type="text", name="url", data-type="url", placeholder="Type the service payload url", data-required="true", ng-model="webhook.url")
|
||||
fieldset
|
||||
input(type="text", name="key", placeholder="Type the service secret key", data-required="true", ng-model="webhook.key")
|
||||
div.webhook-options
|
||||
a.icon.icon-floppy(href="", title="Save Webhook")
|
||||
a.icon.icon-delete(href="", title="Cancel Webhook")
|
||||
div.single-webhook-wrapper
|
||||
a.edit-existing.icon.icon-floppy(href="", title="Save Webhook")
|
||||
a.cancel-existing.icon.icon-delete(href="", title="Cancel Webhook")
|
||||
|
||||
div.visualization-mode
|
||||
div.row
|
||||
div.webhook-service
|
||||
span Github
|
||||
span(ng-bind="webhook.name")
|
||||
div.webhook-url
|
||||
span http://github.kjrw3543m/nwdlkw4m535/ffm
|
||||
a(href="", title="Test history") (See test history)
|
||||
span(ng-bind="webhook.url")
|
||||
a.show-history.toggle-history(href="", title="Toggle history", ng-show="webhook.logs_counter ") (Show history)
|
||||
|
||||
div.webhook-options
|
||||
div.webhook-options-wrapper
|
||||
a.icon.icon-floppy(href="", title="Save Webhook")
|
||||
a.icon.icon-edit(href="", title="Edit Webhook")
|
||||
a.icon.icon-delete(href="", title="Delete Webhook")
|
||||
div.single-webhook-wrapper
|
||||
div.row
|
||||
div.webhook-service
|
||||
span Slack
|
||||
a.test-webhook.icon.icon-check-square(href="", title="Test Webhook")
|
||||
a.edit-webhook.icon.icon-edit(href="", title="Edit Webhook")
|
||||
a.delete-webhook.icon.icon-delete(href="", title="Delete Webhook")
|
||||
|
||||
div.webhooks-history(ng-show="webhook.logs")
|
||||
div.history-single-wrapper(ng-repeat="log in webhook.logs")
|
||||
div.history-single
|
||||
div
|
||||
span.history-response-icon(ng-class="validStatus ? 'history-success' : 'history-error'")
|
||||
span.history-date(ng-bind="log.prettyDate")
|
||||
span.toggle-log.icon.icon-arrow-bottom
|
||||
|
||||
div.history-single-response
|
||||
div.history-single-request-header
|
||||
span Request
|
||||
a.resend-request(href="", title="Resend request", data-log="{{log.id}}")
|
||||
span.icon.icon-reload
|
||||
span Resend request
|
||||
div.history-single-request-body
|
||||
div.response-container
|
||||
span.response-title Headers
|
||||
textarea(name="headers", ng-bind="log.prettySentHeaders")
|
||||
|
||||
div.response-container
|
||||
span.response-title Payload
|
||||
textarea(name="payload", ng-bind="log.prettySentData")
|
||||
|
||||
div.history-single-response-header
|
||||
span Response
|
||||
div.history-single-response-body
|
||||
div.response-container
|
||||
textarea(name="response-data", ng-bind="log.response_data")
|
||||
|
||||
form.new-webhook-form.row.hidden
|
||||
fieldset.webhook-service
|
||||
input(type="text", name="name", placeholder="Type the service name", data-required="true", ng-model="newValue.name")
|
||||
div.webhook-url
|
||||
span http://slack.kjrw3543m/nwdlkw4m535/ffm
|
||||
a(href="", title="Test history") (See test history)
|
||||
div.webhook-url-inputs
|
||||
fieldset
|
||||
input(type="text", name="url", data-type="url", placeholder="Type the service payload url", data-required="true", ng-model="newValue.url")
|
||||
fieldset
|
||||
input(type="text", name="key", placeholder="Type the service secret key", data-required="true", ng-model="newValue.key")
|
||||
div.webhook-options
|
||||
div.webhook-options-wrapper
|
||||
a.icon.icon-floppy(href="", title="Save Webhook")
|
||||
a.icon.icon-edit(href="", title="Edit Webhook")
|
||||
a.icon.icon-delete(href="", title="Delete Webhook")
|
||||
div.webhooks-history
|
||||
div.history-single
|
||||
div
|
||||
span.history-response.history-success
|
||||
span.history-date Just now
|
||||
span.icon.icon-arrow-bottom
|
||||
div.history-single
|
||||
div
|
||||
span.history-response.history-error
|
||||
span.history-date Just now
|
||||
span.icon.icon-arrow-bottom
|
||||
a.add-new.icon.icon-floppy(href="", title="Save Webhook")
|
||||
a.cancel-new.icon.icon-delete(href="", title="Cancel Webhook")
|
||||
|
||||
//
|
||||
form
|
||||
fieldset
|
||||
label(for="secret-key") Secret key
|
||||
input(type="text", name="secret-key", ng-model="github.secret", placeholder="Secret key", id="secret-key")
|
||||
|
||||
fieldset
|
||||
.select-input-text(tg-select-input-text)
|
||||
div
|
||||
label(for="payload-url") Payload URL
|
||||
.field-with-option
|
||||
input(type="text", ng-model="github.webhooks_url", name="payload-url", readonly="readonly", placeholder="Payload URL", id="payload-url")
|
||||
.option-wrapper.select-input-content
|
||||
.icon.icon-copy
|
||||
.help-copy Copy to clipboard: Ctrl+C
|
||||
|
||||
button(type="submit", class="hidden")
|
||||
a.button.button-green.submit-button(href="", title="Save") Save
|
||||
|
||||
|
||||
a.help-button(href="https://taiga.io/support/github-integration/", target="_blank")
|
||||
a.help-button(href="https://taiga.io/support/webhooks/", target="_blank")
|
||||
span.icon.icon-help
|
||||
span Do you need help? Check out our support page!
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
%text {font-family: 'opensans-regular', Arial, Helvetica, sans-serif; line-height: 1.3rem;}
|
||||
%bold {font-family: 'opensans-semibold', Arial, Helvetica, sans-serif;}
|
||||
%taiga {font-family: 'taiga';}
|
||||
%mono {font-family: 'courier new', 'monospace';}
|
||||
|
||||
%lightbox {
|
||||
background: rgba($white, .95);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
@mixin slide($max, $overflow, $min: 0) {
|
||||
max-height: $min;
|
||||
transition: max-height .5s ease-in;
|
||||
#{$overflow}: hidden;
|
||||
overflow: #{$overflow};
|
||||
&.open {
|
||||
transition: max-height .5s ease-in;
|
||||
max-height: $max;
|
||||
|
|
|
@ -78,32 +78,53 @@
|
|||
.webhook-url-inputs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
input {
|
||||
flex-basis: 1;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: center;
|
||||
fieldset {
|
||||
flex-grow: 1;
|
||||
margin-right: .3rem;
|
||||
}
|
||||
}
|
||||
|
||||
.webhooks-history{
|
||||
@include slide(1000px, hidden, $min: 0);
|
||||
}
|
||||
|
||||
.history-single-wrapper {
|
||||
border-bottom: 1px solid $whitish;
|
||||
margin-left: 22%;
|
||||
}
|
||||
.history-single {
|
||||
align-items: center;
|
||||
border-bottom: 1px solid $whitish;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-left: 22%;
|
||||
padding: .5rem;
|
||||
transition: background .2s linear;
|
||||
&:hover {
|
||||
background: rgba($fresh-taiga, .1);
|
||||
transition: background .2s linear;
|
||||
}
|
||||
&.history-single-open {
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
.history-response {
|
||||
.icon-arrow-bottom {
|
||||
transform: rotate(180deg);
|
||||
transition: transform .3s linear;
|
||||
}
|
||||
}
|
||||
.icon-arrow-bottom {
|
||||
transform: rotate(0);
|
||||
transition: transform .3s linear;
|
||||
}
|
||||
}
|
||||
.history-response-icon {
|
||||
background: $gray;
|
||||
border-radius: 25%;
|
||||
display: inline-block;
|
||||
height: .8rem;
|
||||
margin-right: .5rem;
|
||||
vertical-align: middle;
|
||||
width: .8rem;
|
||||
&.history-success {
|
||||
background: $fresh-taiga;
|
||||
|
@ -112,4 +133,64 @@
|
|||
background: $red;
|
||||
}
|
||||
}
|
||||
.history-single-response {
|
||||
@include slide(1000px, hidden, $min: 0);
|
||||
&.open {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
.history-single-request-header,
|
||||
.history-single-response-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: .5rem 0;
|
||||
span:first-child {
|
||||
@extend %bold;
|
||||
color: $gray-light;
|
||||
}
|
||||
a {
|
||||
@extend %small;
|
||||
color: $gray-light;
|
||||
&:hover {
|
||||
color: $fresh-taiga;
|
||||
transition: color .2s linear;
|
||||
}
|
||||
}
|
||||
.icon {
|
||||
margin-right: .3rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
.history-single-request-body,
|
||||
.history-single-response-body {
|
||||
.response-container {
|
||||
@extend %mono;
|
||||
align-content: center;
|
||||
align-items: center;
|
||||
background: $whitish;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
span {
|
||||
@extend %small;
|
||||
color: $gray-light;
|
||||
flex-basis: 20%;
|
||||
flex-grow: 1;
|
||||
flex-shrink: 0;
|
||||
text-align: center;
|
||||
}
|
||||
textarea {
|
||||
@extend %mono;
|
||||
border: 0;
|
||||
flex-grow: 2;
|
||||
min-height: 7.5rem;
|
||||
}
|
||||
}
|
||||
.history-single-response-body {
|
||||
textarea {
|
||||
min-height: 10rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue