feat: pull request generation
This commit is contained in:
@ -101,7 +101,7 @@ func (f *Form) Field(name string) *Field {
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
panic(errors.Errorf("no form field named '%s'", name))
|
||||
}
|
||||
|
||||
func (f *Form) Error(name string) (ValidationError, bool) {
|
||||
|
51
internal/http/handler/webui/common/component/app_page.templ
Normal file
51
internal/http/handler/webui/common/component/app_page.templ
Normal file
@ -0,0 +1,51 @@
|
||||
package component
|
||||
|
||||
type AppPageOptions struct {
|
||||
PageOptions []PageOptionFunc
|
||||
}
|
||||
|
||||
type AppPageOptionFunc func(opts *AppPageOptions)
|
||||
|
||||
func WithPageOptions(funcs ...PageOptionFunc) AppPageOptionFunc {
|
||||
return func(opts *AppPageOptions) {
|
||||
opts.PageOptions = funcs
|
||||
}
|
||||
}
|
||||
|
||||
func NewAppPageOptions(funcs ...AppPageOptionFunc) *AppPageOptions {
|
||||
opts := &AppPageOptions{
|
||||
PageOptions: make([]PageOptionFunc, 0),
|
||||
}
|
||||
for _, fn := range funcs {
|
||||
fn(opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
templ AppPage(funcs ...AppPageOptionFunc) {
|
||||
{{ opts := NewAppPageOptions(funcs...) }}
|
||||
@Page(opts.PageOptions...) {
|
||||
<div class="container is-fluid">
|
||||
<section class="section">
|
||||
<div class="level is-mobile">
|
||||
<div class="level-left">
|
||||
<h1 class="title is-size-1"><span class="has-text-info-light">Clear</span><span>Case</span></h1>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="buttons is-right level-item">
|
||||
<a class="button is-medium" href={ BaseURL(ctx, WithPath("/auth/logout")) }><span class="icon"><i class="fa fa-sign-out-alt"></i></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs is-large is-fullwidth is-boxed">
|
||||
<ul>
|
||||
<li class={ templ.KV("is-active", MatchPath(ctx, "/issue/")) }><a href={ BaseURL(ctx, WithPath("/issue")) }><span class="icon"><i class="fa fa-plus"></i></span>Nouvelle demande</a></li>
|
||||
// <li><a href={ BaseURL(ctx, WithPath("/issue")) }><span class="icon"><i class="fa fa-edit"></i></span>Éditer une demande</a></li>
|
||||
<li class={ templ.KV("is-active", MatchPath(ctx, "/pullrequest/")) }><a href={ CurrentURL(ctx, WithPath("/pullrequest")) }><span class="icon"><i class="fas fa-code-branch"></i></span>Éditer une PR</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{ children... }
|
||||
</section>
|
||||
</div>
|
||||
}
|
||||
}
|
160
internal/http/handler/webui/common/component/app_page_templ.go
Normal file
160
internal/http/handler/webui/common/component/app_page_templ.go
Normal file
@ -0,0 +1,160 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.819
|
||||
package component
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
type AppPageOptions struct {
|
||||
PageOptions []PageOptionFunc
|
||||
}
|
||||
|
||||
type AppPageOptionFunc func(opts *AppPageOptions)
|
||||
|
||||
func WithPageOptions(funcs ...PageOptionFunc) AppPageOptionFunc {
|
||||
return func(opts *AppPageOptions) {
|
||||
opts.PageOptions = funcs
|
||||
}
|
||||
}
|
||||
|
||||
func NewAppPageOptions(funcs ...AppPageOptionFunc) *AppPageOptions {
|
||||
opts := &AppPageOptions{
|
||||
PageOptions: make([]PageOptionFunc, 0),
|
||||
}
|
||||
for _, fn := range funcs {
|
||||
fn(opts)
|
||||
}
|
||||
return opts
|
||||
}
|
||||
|
||||
func AppPage(funcs ...AppPageOptionFunc) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
opts := NewAppPageOptions(funcs...)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"container is-fluid\"><section class=\"section\"><div class=\"level is-mobile\"><div class=\"level-left\"><h1 class=\"title is-size-1\"><span class=\"has-text-info-light\">Clear</span><span>Case</span></h1></div><div class=\"level-right\"><div class=\"buttons is-right level-item\"><a class=\"button is-medium\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL = BaseURL(ctx, WithPath("/auth/logout"))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><span class=\"icon\"><i class=\"fa fa-sign-out-alt\"></i></span></a></div></div></div><div class=\"tabs is-large is-fullwidth is-boxed\"><ul>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 = []any{templ.KV("is-active", MatchPath(ctx, "/issue/"))}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var4...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<li class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var4).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/http/handler/webui/common/component/app_page.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL = BaseURL(ctx, WithPath("/issue"))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "\"><span class=\"icon\"><i class=\"fa fa-plus\"></i></span>Nouvelle demande</a></li>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 = []any{templ.KV("is-active", MatchPath(ctx, "/pullrequest/"))}
|
||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var7...)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "<li class=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var8 string
|
||||
templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(templ.CSSClasses(templ_7745c5c3_Var7).String())
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/http/handler/webui/common/component/app_page.templ`, Line: 1, Col: 0}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "\"><a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var9 templ.SafeURL = CurrentURL(ctx, WithPath("/pullrequest"))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var9)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\"><span class=\"icon\"><i class=\"fas fa-code-branch\"></i></span>Éditer une PR</a></li></ul></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "</section></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = Page(opts.PageOptions...).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
@ -40,7 +40,7 @@ func NewIssueSummaryForm() *form.Form {
|
||||
form.NewField(
|
||||
"project",
|
||||
form.Attrs{},
|
||||
form.NonEmpty("Ce champs ne doit pas être vide."),
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"summary",
|
||||
@ -85,31 +85,21 @@ func NewIssueForm() *form.Form {
|
||||
}
|
||||
|
||||
templ IssuePage(vmodel IssuePageVModel) {
|
||||
@common.Page(common.WithTitle("Nouvelle demande")) {
|
||||
<div class="container is-fluid">
|
||||
<section class="section">
|
||||
<div class="level is-mobile">
|
||||
<div class="level-left">
|
||||
<h1 class="title is-size-1"><span class="has-text-info-light">Clear</span><span>Case</span></h1>
|
||||
</div>
|
||||
<div class="level-right">
|
||||
<div class="buttons is-right level-item">
|
||||
<a class="button is-medium" href={ common.BaseURL(ctx, common.WithPath("/auth/logout")) }><span class="icon"><i class="fa fa-sign-out-alt"></i></span></a>
|
||||
</div>
|
||||
</div>
|
||||
@common.AppPage(common.WithPageOptions(
|
||||
common.WithTitle("Nouvelle demande"),
|
||||
)) {
|
||||
if vmodel.IssueURL != "" {
|
||||
<article class="message is-primary">
|
||||
<div class="message-header">
|
||||
<p>Demande créée !</p>
|
||||
<button class="delete" aria-label="delete" hx-on:click="onCloseMessage(this)"></button>
|
||||
</div>
|
||||
if vmodel.IssueURL != "" {
|
||||
<article class="message is-primary">
|
||||
<div class="message-header">
|
||||
<p>Demande créée !</p>
|
||||
<button class="delete" aria-label="delete" hx-on:click="onCloseMessage(this)"></button>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
Votre demande a été créée et est disponible à l'adresse suivante:
|
||||
<a href={ templ.SafeURL(vmodel.IssueURL) } target="_blank"><code>{ vmodel.IssueURL }</code></a>.
|
||||
</div>
|
||||
</article>
|
||||
<script type="text/javascript">
|
||||
<div class="message-body">
|
||||
Votre demande a été créée et est disponible à l'adresse suivante:
|
||||
<a href={ templ.SafeURL(vmodel.IssueURL) } target="_blank"><code>{ vmodel.IssueURL }</code></a>.
|
||||
</div>
|
||||
</article>
|
||||
<script type="text/javascript">
|
||||
function clearSummary(projectId) {
|
||||
sessionStorage.removeItem(`summary-${projectId}`)
|
||||
}
|
||||
@ -117,83 +107,81 @@ templ IssuePage(vmodel IssuePageVModel) {
|
||||
window.open(issueUrl, "_blank");
|
||||
}
|
||||
</script>
|
||||
@templ.JSFuncCall("clearSummary", vmodel.SelectedProjectID)
|
||||
@templ.JSFuncCall("openIssue", vmodel.IssueURL)
|
||||
}
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<form id="summary-form" action={ common.CurrentURL(ctx) } method="put" hx-disabled-elt="textarea, input, select, button" hx-on:htmx:before-send="savePreferredProject()" hx-indicator="#generation-progress">
|
||||
<h2 class="title is-size-3">Résumé de la demande</h2>
|
||||
@common.FormSelect(
|
||||
vmodel.SummaryForm, "issue-project", "project", "Projet",
|
||||
common.WithOptions(projectsToOptions(vmodel.Projects)...),
|
||||
common.WithAttrs(
|
||||
"hx-get", string(common.CurrentURL(ctx, common.WithoutValues("project", "*"))),
|
||||
"hx-target", "body",
|
||||
"hx-push-url", "true",
|
||||
),
|
||||
)
|
||||
@common.FormTextarea(
|
||||
vmodel.SummaryForm, "issue-summary", "summary", "Résumé",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onSummaryChange(event)",
|
||||
),
|
||||
)
|
||||
<details class="my-3">
|
||||
<summary class="is-clickable">Paramètres avancés</summary>
|
||||
@common.FormTextarea(
|
||||
vmodel.SummaryForm, "issue-template", "template", "Surcharger le modèle de demande",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onIssueTemplateChange(event)",
|
||||
),
|
||||
)
|
||||
</details>
|
||||
<div class="buttons is-right">
|
||||
<button type="submit" class="button is-info is-large">
|
||||
<span class="icon">
|
||||
<i class="fa fa-robot"></i>
|
||||
</span>
|
||||
<span>Générer</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
@templ.JSFuncCall("clearSummary", vmodel.SelectedProjectID)
|
||||
@templ.JSFuncCall("openIssue", vmodel.IssueURL)
|
||||
}
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<form id="summary-form" action={ common.CurrentURL(ctx) } method="put" hx-disabled-elt="textarea, input, select, button" hx-on:htmx:before-send="savePreferredProject()" hx-indicator="#generation-progress">
|
||||
<h2 class="title is-size-3">Résumé de la demande</h2>
|
||||
@common.FormSelect(
|
||||
vmodel.SummaryForm, "issue-project", "project", "Projet",
|
||||
common.WithOptions(projectsToOptions(vmodel.Projects)...),
|
||||
common.WithAttrs(
|
||||
"hx-get", string(common.CurrentURL(ctx, common.WithoutValues("project", "*"))),
|
||||
"hx-target", "body",
|
||||
"hx-push-url", "true",
|
||||
),
|
||||
)
|
||||
@common.FormTextarea(
|
||||
vmodel.SummaryForm, "issue-summary", "summary", "Résumé",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onSummaryChange(event)",
|
||||
),
|
||||
)
|
||||
<details class="my-3">
|
||||
<summary class="is-clickable">Paramètres avancés</summary>
|
||||
@common.FormTextarea(
|
||||
vmodel.SummaryForm, "issue-template", "template", "Surcharger le modèle de demande",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onIssueTemplateChange(event)",
|
||||
),
|
||||
)
|
||||
</details>
|
||||
<div class="buttons is-right">
|
||||
<button type="submit" class="button is-info is-large">
|
||||
<span class="icon">
|
||||
<i class="fa fa-robot"></i>
|
||||
</span>
|
||||
<span>Générer</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="title is-size-3">Votre demande</h2>
|
||||
<form action={ common.CurrentURL(ctx) } method="post" hx-disabled-elt="textarea, input, select, button" hx-indicator="#generation-progress">
|
||||
@common.FormField(vmodel.IssueForm, "issue-title", "title", "Titre")
|
||||
@common.FormTextarea(vmodel.IssueForm, "issue-body", "body", "Corps")
|
||||
<div class="buttons is-right">
|
||||
<button type="submit" class="button is-info is-large">
|
||||
<span class="icon">
|
||||
<i class="fa fa-rocket"></i>
|
||||
</span>
|
||||
<span>Créer</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="title is-size-3">Votre demande</h2>
|
||||
<form action={ common.CurrentURL(ctx) } method="post" hx-disabled-elt="textarea, input, select, button" hx-indicator="#generation-progress">
|
||||
@common.FormField(vmodel.IssueForm, "issue-title", "title", "Titre")
|
||||
@common.FormTextarea(vmodel.IssueForm, "issue-body", "body", "Corps")
|
||||
<div class="buttons is-right">
|
||||
<button type="submit" class="button is-info is-large">
|
||||
<span class="icon">
|
||||
<i class="fa fa-rocket"></i>
|
||||
</span>
|
||||
<span>Créer</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<progress id="generation-progress" class="htmx-indicator progress"></progress>
|
||||
if vmodel.IssueTips != "" {
|
||||
{{ html := markdownToHTML(ctx, vmodel.IssueTips) }}
|
||||
if html != "" {
|
||||
<article class="message is-info mt-5">
|
||||
<div class="message-header">
|
||||
<p><span class="icon"><i class="fa fa-lightbulb"></i></span>Questionnements</p>
|
||||
<button class="delete" aria-label="delete" hx-on:click="onCloseMessage(this)"></button>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
<div class="content">
|
||||
<p>Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre demande:</p>
|
||||
@templ.Raw(html)
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
}
|
||||
}
|
||||
</section>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<progress id="generation-progress" class="htmx-indicator progress"></progress>
|
||||
if vmodel.IssueTips != "" {
|
||||
{{ html := markdownToHTML(ctx, vmodel.IssueTips) }}
|
||||
if html != "" {
|
||||
<article class="message is-info mt-5">
|
||||
<div class="message-header">
|
||||
<p><span class="icon"><i class="fa fa-lightbulb"></i></span>Questionnements</p>
|
||||
<button class="delete" aria-label="delete" hx-on:click="onCloseMessage(this)"></button>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
<div class="content">
|
||||
<p>Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre demande:</p>
|
||||
@templ.Raw(html)
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
}
|
||||
}
|
||||
<script type="text/javascript">
|
||||
function onCloseMessage(closeElement) {
|
||||
closeElement.closest('.message').style.display = 'none';
|
||||
|
@ -48,7 +48,7 @@ func NewIssueSummaryForm() *form.Form {
|
||||
form.NewField(
|
||||
"project",
|
||||
form.Attrs{},
|
||||
form.NonEmpty("Ce champs ne doit pas être vide."),
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"summary",
|
||||
@ -125,43 +125,30 @@ func IssuePage(vmodel IssuePageVModel) templ.Component {
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div class=\"container is-fluid\"><section class=\"section\"><div class=\"level is-mobile\"><div class=\"level-left\"><h1 class=\"title is-size-1\"><span class=\"has-text-info-light\">Clear</span><span>Case</span></h1></div><div class=\"level-right\"><div class=\"buttons is-right level-item\"><a class=\"button is-medium\" href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL = common.BaseURL(ctx, common.WithPath("/auth/logout"))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><span class=\"icon\"><i class=\"fa fa-sign-out-alt\"></i></span></a></div></div></div>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if vmodel.IssueURL != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<article class=\"message is-primary\"><div class=\"message-header\"><p>Demande créée !</p><button class=\"delete\" aria-label=\"delete\" hx-on:click=\"onCloseMessage(this)\"></button></div><div class=\"message-body\">Votre demande a été créée et est disponible à l'adresse suivante: <a href=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<article class=\"message is-primary\"><div class=\"message-header\"><p>Demande créée !</p><button class=\"delete\" aria-label=\"delete\" hx-on:click=\"onCloseMessage(this)\"></button></div><div class=\"message-body\">Votre demande a été créée et est disponible à l'adresse suivante: <a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 templ.SafeURL = templ.SafeURL(vmodel.IssueURL)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var4)))
|
||||
var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(vmodel.IssueURL)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "\" target=\"_blank\"><code>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" target=\"_blank\"><code>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(vmodel.IssueURL)
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(vmodel.IssueURL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/http/handler/webui/issue/component/issue_page.templ`, Line: 109, Col: 89}
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/http/handler/webui/issue/component/issue_page.templ`, Line: 99, Col: 87}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, "</code></a>.</div></article><script type=\"text/javascript\">\n\t\t\t\t\t\tfunction clearSummary(projectId) {\n\t\t\t\t\t\t\tsessionStorage.removeItem(`summary-${projectId}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfunction openIssue(issueUrl) {\n\t\t\t\t\t\t\twindow.open(issueUrl, \"_blank\");\n\t\t\t\t\t\t}\n\t\t\t\t\t</script> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</code></a>.</div></article><script type=\"text/javascript\">\n\t\t\t\t\t\tfunction clearSummary(projectId) {\n\t\t\t\t\t\t\tsessionStorage.removeItem(`summary-${projectId}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfunction openIssue(issueUrl) {\n\t\t\t\t\t\t\twindow.open(issueUrl, \"_blank\");\n\t\t\t\t\t\t}\n\t\t\t\t\t</script> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -169,7 +156,7 @@ func IssuePage(vmodel IssuePageVModel) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, " ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -178,16 +165,16 @@ func IssuePage(vmodel IssuePageVModel) templ.Component {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<div class=\"columns\"><div class=\"column is-4\"><form id=\"summary-form\" action=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " <div class=\"columns\"><div class=\"column is-4\"><form id=\"summary-form\" action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL = common.CurrentURL(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
var templ_7745c5c3_Var5 templ.SafeURL = common.CurrentURL(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "\" method=\"put\" hx-disabled-elt=\"textarea, input, select, button\" hx-on:htmx:before-send=\"savePreferredProject()\" hx-indicator=\"#generation-progress\"><h2 class=\"title is-size-3\">Résumé de la demande</h2>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" method=\"put\" hx-disabled-elt=\"textarea, input, select, button\" hx-on:htmx:before-send=\"savePreferredProject()\" hx-indicator=\"#generation-progress\"><h2 class=\"title is-size-3\">Résumé de la demande</h2>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -212,7 +199,7 @@ func IssuePage(vmodel IssuePageVModel) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "<details class=\"my-3\"><summary class=\"is-clickable\">Paramètres avancés</summary>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<details class=\"my-3\"><summary class=\"is-clickable\">Paramètres avancés</summary>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -225,16 +212,16 @@ func IssuePage(vmodel IssuePageVModel) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "</details><div class=\"buttons is-right\"><button type=\"submit\" class=\"button is-info is-large\"><span class=\"icon\"><i class=\"fa fa-robot\"></i></span> <span>Générer</span></button></div></form></div><div class=\"column\"><h2 class=\"title is-size-3\">Votre demande</h2><form action=\"")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</details><div class=\"buttons is-right\"><button type=\"submit\" class=\"button is-info is-large\"><span class=\"icon\"><i class=\"fa fa-robot\"></i></span> <span>Générer</span></button></div></form></div><div class=\"column\"><h2 class=\"title is-size-3\">Votre demande</h2><form action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var7 templ.SafeURL = common.CurrentURL(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var7)))
|
||||
var templ_7745c5c3_Var6 templ.SafeURL = common.CurrentURL(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "\" method=\"post\" hx-disabled-elt=\"textarea, input, select, button\" hx-indicator=\"#generation-progress\">")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" method=\"post\" hx-disabled-elt=\"textarea, input, select, button\" hx-indicator=\"#generation-progress\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -246,14 +233,14 @@ func IssuePage(vmodel IssuePageVModel) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "<div class=\"buttons is-right\"><button type=\"submit\" class=\"button is-info is-large\"><span class=\"icon\"><i class=\"fa fa-rocket\"></i></span> <span>Créer</span></button></div></form></div></div><progress id=\"generation-progress\" class=\"htmx-indicator progress\"></progress> ")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"buttons is-right\"><button type=\"submit\" class=\"button is-info is-large\"><span class=\"icon\"><i class=\"fa fa-rocket\"></i></span> <span>Créer</span></button></div></form></div></div><progress id=\"generation-progress\" class=\"htmx-indicator progress\"></progress> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if vmodel.IssueTips != "" {
|
||||
html := markdownToHTML(ctx, vmodel.IssueTips)
|
||||
if html != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "<article class=\"message is-info mt-5\"><div class=\"message-header\"><p><span class=\"icon\"><i class=\"fa fa-lightbulb\"></i></span>Questionnements</p><button class=\"delete\" aria-label=\"delete\" hx-on:click=\"onCloseMessage(this)\"></button></div><div class=\"message-body\"><div class=\"content\"><p>Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre demande:</p>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<article class=\"message is-info mt-5\"><div class=\"message-header\"><p><span class=\"icon\"><i class=\"fa fa-lightbulb\"></i></span>Questionnements</p><button class=\"delete\" aria-label=\"delete\" hx-on:click=\"onCloseMessage(this)\"></button></div><div class=\"message-body\"><div class=\"content\"><p>Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre demande:</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
@ -261,19 +248,21 @@ func IssuePage(vmodel IssuePageVModel) templ.Component {
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "</div></div></article>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div></div></article>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "</section></div><script type=\"text/javascript\">\n\t\tfunction onCloseMessage(closeElement) {\n\t\t\tcloseElement.closest('.message').style.display = 'none';\n\t\t}\n\n\t\tfunction onSummaryChange(evt) {\n\t\t\tconst summary = evt.currentTarget.value;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tsessionStorage.setItem(`summary-${projectId}`, summary);\n\t\t}\n\n\t\tfunction onIssueTemplateChange(evt) {\n\t\t\tconst issueTemplate = evt.currentTarget.value;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tlocalStorage.setItem(`issue-template-${projectId}`, issueTemplate);\n\t\t}\n\n\t\tfunction savePreferredProject() {\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tlocalStorage.setItem(`preferred-project`, projectId);\n\t\t}\n\n\t\tfunction restorePreferredProject() {\n\t\t\tconst preferredProject = localStorage.getItem(`preferred-project`);\n\t\t\tif (!preferredProject) return;\n\t\t\tconst projectElement = document.getElementById(\"issue-project\");\n\t\t\tif (!projectElement) return;\n\t\t\tif (preferredProject === projectElement.value) return;\n\t\t\tprojectElement.value = preferredProject;\n\t\t\tprojectElement.dispatchEvent(new Event('change'));\n\t\t}\n\n\t\tfunction restoreLastSummary() {\n\t\t\tconst summaryTextarea = document.getElementById(\"issue-summary\");\n\t\t\tif (!summaryTextarea) return;\n const summary = summaryTextarea.value;\n\t\t\tif (summary !== \"\") return;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tif (!projectId) return;\n\t\t\tconst savedSummary = sessionStorage.getItem(`summary-${projectId}`);\n\t\t\tif (!savedSummary) return;\n\t\t\tsummaryTextarea.value = savedSummary;\n\t\t}\n\n\t\tfunction restoreIssueTemplate() {\n\t\t\tconst issueTemplateTextarea = document.getElementById(\"issue-template\");\n\t\t\tif (!issueTemplateTextarea) return;\n const issueTemplate = issueTemplateTextarea.value;\n\t\t\tif (issueTemplate !== \"\") return;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tif (!projectId) return;\n\t\t\tconst savedIssueTemplate = localStorage.getItem(`issue-template-${projectId}`);\n\t\t\tif (!savedIssueTemplate) return;\n\t\t\tissueTemplateTextarea.value = savedIssueTemplate;\n\t\t}\n\n\t\thtmx.onLoad(function(){\n\t\t\trestoreLastSummary();\n\t\t\trestorePreferredProject();\n\t\t\trestoreIssueTemplate();\n })\n\t\t</script>")
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " <script type=\"text/javascript\">\n\t\tfunction onCloseMessage(closeElement) {\n\t\t\tcloseElement.closest('.message').style.display = 'none';\n\t\t}\n\n\t\tfunction onSummaryChange(evt) {\n\t\t\tconst summary = evt.currentTarget.value;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tsessionStorage.setItem(`summary-${projectId}`, summary);\n\t\t}\n\n\t\tfunction onIssueTemplateChange(evt) {\n\t\t\tconst issueTemplate = evt.currentTarget.value;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tlocalStorage.setItem(`issue-template-${projectId}`, issueTemplate);\n\t\t}\n\n\t\tfunction savePreferredProject() {\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tlocalStorage.setItem(`preferred-project`, projectId);\n\t\t}\n\n\t\tfunction restorePreferredProject() {\n\t\t\tconst preferredProject = localStorage.getItem(`preferred-project`);\n\t\t\tif (!preferredProject) return;\n\t\t\tconst projectElement = document.getElementById(\"issue-project\");\n\t\t\tif (!projectElement) return;\n\t\t\tif (preferredProject === projectElement.value) return;\n\t\t\tprojectElement.value = preferredProject;\n\t\t\tprojectElement.dispatchEvent(new Event('change'));\n\t\t}\n\n\t\tfunction restoreLastSummary() {\n\t\t\tconst summaryTextarea = document.getElementById(\"issue-summary\");\n\t\t\tif (!summaryTextarea) return;\n const summary = summaryTextarea.value;\n\t\t\tif (summary !== \"\") return;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tif (!projectId) return;\n\t\t\tconst savedSummary = sessionStorage.getItem(`summary-${projectId}`);\n\t\t\tif (!savedSummary) return;\n\t\t\tsummaryTextarea.value = savedSummary;\n\t\t}\n\n\t\tfunction restoreIssueTemplate() {\n\t\t\tconst issueTemplateTextarea = document.getElementById(\"issue-template\");\n\t\t\tif (!issueTemplateTextarea) return;\n const issueTemplate = issueTemplateTextarea.value;\n\t\t\tif (issueTemplate !== \"\") return;\n\t\t\tconst projectId = document.getElementById(\"issue-project\").value;\n\t\t\tif (!projectId) return;\n\t\t\tconst savedIssueTemplate = localStorage.getItem(`issue-template-${projectId}`);\n\t\t\tif (!savedIssueTemplate) return;\n\t\t\tissueTemplateTextarea.value = savedIssueTemplate;\n\t\t}\n\n\t\thtmx.onLoad(function(){\n\t\t\trestoreLastSummary();\n\t\t\trestorePreferredProject();\n\t\t\trestoreIssueTemplate();\n })\n\t\t</script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = common.Page(common.WithTitle("Nouvelle demande")).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
templ_7745c5c3_Err = common.AppPage(common.WithPageOptions(
|
||||
common.WithTitle("Nouvelle demande"),
|
||||
)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
mux *http.ServeMux
|
||||
issueManager *service.IssueManager
|
||||
mux *http.ServeMux
|
||||
forge *service.ForgeManager
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
@ -16,10 +16,10 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func NewHandler(issueManager *service.IssueManager) *Handler {
|
||||
func NewHandler(forge *service.ForgeManager) *Handler {
|
||||
h := &Handler{
|
||||
mux: http.NewServeMux(),
|
||||
issueManager: issueManager,
|
||||
mux: http.NewServeMux(),
|
||||
forge: forge,
|
||||
}
|
||||
|
||||
h.mux.HandleFunc("GET /", h.getIssuePage)
|
||||
|
@ -47,7 +47,7 @@ func (h *Handler) fillIssuePageVModel(r *http.Request) (*component.IssuePageVMod
|
||||
func (h *Handler) fillIssuePageProjects(ctx context.Context, vmodel *component.IssuePageVModel, r *http.Request) error {
|
||||
user := httpCtx.User(ctx)
|
||||
|
||||
projects, err := h.issueManager.GetUserProjects(ctx, user)
|
||||
projects, err := h.forge.GetUserProjects(ctx, user)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
@ -114,7 +114,7 @@ func (h *Handler) handleIssueSummaryForm(w http.ResponseWriter, r *http.Request)
|
||||
|
||||
issueTemplate = strings.TrimSpace(issueTemplate)
|
||||
|
||||
issueTitle, issueBody, issueTips, err := h.issueManager.GenerateIssue(ctx, httpCtx.User(ctx), projectID, issueSummary, issueTemplate)
|
||||
issueTitle, issueBody, issueTips, err := h.forge.GenerateIssue(ctx, httpCtx.User(ctx), projectID, issueSummary, issueTemplate)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
@ -168,7 +168,7 @@ func (h *Handler) handleIssueForm(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
user := httpCtx.User(ctx)
|
||||
|
||||
issueURL, err := h.issueManager.CreateIssue(ctx, user, projectID, title, body)
|
||||
issueURL, err := h.forge.CreateIssue(ctx, user, projectID, title, body)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
|
@ -0,0 +1,313 @@
|
||||
package component
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/core/model"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/http/form"
|
||||
common "forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/common/component"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/yuin/goldmark"
|
||||
"log/slog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PullRequestPageVModel struct {
|
||||
PullRequestURL string
|
||||
SummaryForm *form.Form
|
||||
PullRequestForm *form.Form
|
||||
PullRequestTips string
|
||||
Projects []*model.Project
|
||||
PullRequests []*model.PullRequest
|
||||
SelectedProjectID string
|
||||
SelectedPullRequestID string
|
||||
}
|
||||
|
||||
const summaryPlaceholder = `
|
||||
Décrivez rapidement les modifications apportées par la PR, ClearCase utilisera le modèle de PR présent dans le dépôt (ou un modèle par défaut) afin de générer une version mise en forme et complétée.
|
||||
|
||||
Afin de fournir plus d'information de contexte au LLM, vous pouvez faire référence à d'autres tickets du dépôt via un ou plusieurs '#<pr_id>' et/ou des chemins vers des fichiers présents dans celui ci.
|
||||
`
|
||||
|
||||
const bodyPlaceholder = `
|
||||
Une fois votre PR générée, vous pourrez l'éditer puis la créer directement en cliquant sur le bouton 'Mettre à jour' ci-dessous.
|
||||
`
|
||||
|
||||
const prTemplatePlaceholder = `
|
||||
Vous pouvez surcharger le modèle de PR fourni par le projet en remplissant ce champ.
|
||||
`
|
||||
|
||||
func NewPullRequestSummaryForm() *form.Form {
|
||||
return form.New(
|
||||
form.NewField(
|
||||
"project",
|
||||
form.Attrs{},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"pullrequest",
|
||||
form.Attrs{},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"summary",
|
||||
form.Attrs{
|
||||
"type": "textarea",
|
||||
"rows": "20",
|
||||
"placeholder": strings.TrimSpace(summaryPlaceholder),
|
||||
},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"template",
|
||||
form.Attrs{
|
||||
"type": "textarea",
|
||||
"rows": "20",
|
||||
"placeholder": strings.TrimSpace(prTemplatePlaceholder),
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func NewPullRequestForm() *form.Form {
|
||||
return form.New(
|
||||
form.NewField(
|
||||
"title",
|
||||
form.Attrs{
|
||||
"type": "text",
|
||||
"placeholder": "Écrivez le résumé de votre demande et cliquez sur 'Générer' pour remplir automatiquement ces champs.",
|
||||
},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"body",
|
||||
form.Attrs{
|
||||
"type": "textarea",
|
||||
"rows": "20",
|
||||
"placeholder": strings.TrimSpace(bodyPlaceholder),
|
||||
},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
templ PullRequestPage(vmodel PullRequestPageVModel) {
|
||||
@common.AppPage(common.WithPageOptions(
|
||||
common.WithTitle("Éditer une PR"),
|
||||
)) {
|
||||
if vmodel.PullRequestURL != "" {
|
||||
<article class="message is-primary">
|
||||
<div class="message-header">
|
||||
<p>Pull Request modifiée !</p>
|
||||
<button class="delete" aria-label="delete" hx-on:click="onCloseMessage(this)"></button>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
Votre PR a été mise à jour et est disponible à l'adresse suivante:
|
||||
<a href={ templ.SafeURL(vmodel.PullRequestURL) } target="_blank"><code>{ vmodel.PullRequestURL }</code></a>.
|
||||
</div>
|
||||
</article>
|
||||
<script type="text/javascript">
|
||||
function clearSummary(projectId) {
|
||||
sessionStorage.removeItem(`pr-summary-${projectId}`)
|
||||
}
|
||||
function openPR(prUrl) {
|
||||
window.open(prUrl, "_blank");
|
||||
}
|
||||
</script>
|
||||
@templ.JSFuncCall("clearSummary", vmodel.SelectedProjectID)
|
||||
@templ.JSFuncCall("openPR", vmodel.PullRequestURL)
|
||||
}
|
||||
<div class="columns">
|
||||
<div class="column is-4">
|
||||
<form id="summary-form" action={ common.CurrentURL(ctx) } method="put" hx-disabled-elt="textarea, input, select, button" hx-on:htmx:before-send="savePreferred()" hx-indicator="#generation-progress">
|
||||
<h2 class="title is-size-3">Résumé de la PR</h2>
|
||||
@common.FormSelect(
|
||||
vmodel.SummaryForm, "pr-project", "project", "Projet",
|
||||
common.WithOptions(projectsToOptions(vmodel.Projects)...),
|
||||
common.WithAttrs(
|
||||
"hx-get", string(common.CurrentURL(ctx, common.WithoutValues("project", "*"))),
|
||||
"hx-target", "body",
|
||||
"hx-push-url", "true",
|
||||
),
|
||||
)
|
||||
@common.FormSelect(
|
||||
vmodel.SummaryForm, "pr-pullrequest", "pullrequest", "PR",
|
||||
common.WithOptions(pullRequestsToOptions(vmodel.PullRequests)...),
|
||||
common.WithAttrs(
|
||||
"hx-get", string(common.CurrentURL(ctx, common.WithoutValues("pullrequest", "*"))),
|
||||
"hx-target", "body",
|
||||
"hx-push-url", "true",
|
||||
),
|
||||
)
|
||||
@common.FormTextarea(
|
||||
vmodel.SummaryForm, "pr-summary", "summary", "Résumé",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onSummaryChange(event)",
|
||||
),
|
||||
)
|
||||
<details class="my-3">
|
||||
<summary class="is-clickable">Paramètres avancés</summary>
|
||||
@common.FormTextarea(
|
||||
vmodel.SummaryForm, "pr-template", "template", "Surcharger le modèle de demande",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onPullRequestTemplateChange(event)",
|
||||
),
|
||||
)
|
||||
</details>
|
||||
<div class="buttons is-right">
|
||||
<button type="submit" class="button is-info is-large">
|
||||
<span class="icon">
|
||||
<i class="fa fa-robot"></i>
|
||||
</span>
|
||||
<span>Générer</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="column">
|
||||
<h2 class="title is-size-3">Votre PR</h2>
|
||||
<form action={ common.CurrentURL(ctx) } method="post" hx-disabled-elt="textarea, input, select, button" hx-indicator="#generation-progress">
|
||||
@common.FormField(vmodel.PullRequestForm, "pr-title", "title", "Titre")
|
||||
@common.FormTextarea(vmodel.PullRequestForm, "pr-body", "body", "Corps")
|
||||
<div class="buttons is-right">
|
||||
<button type="submit" class="button is-info is-large">
|
||||
<span class="icon">
|
||||
<i class="fa fa-rocket"></i>
|
||||
</span>
|
||||
<span>Mettre à jour</span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<progress id="generation-progress" class="htmx-indicator progress"></progress>
|
||||
if vmodel.PullRequestTips != "" {
|
||||
{{ html := markdownToHTML(ctx, vmodel.PullRequestTips) }}
|
||||
if html != "" {
|
||||
<article class="message is-info mt-5">
|
||||
<div class="message-header">
|
||||
<p><span class="icon"><i class="fa fa-lightbulb"></i></span>Questionnements</p>
|
||||
<button class="delete" aria-label="delete" hx-on:click="onCloseMessage(this)"></button>
|
||||
</div>
|
||||
<div class="message-body">
|
||||
<div class="content">
|
||||
<p>Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre PR:</p>
|
||||
@templ.Raw(html)
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
}
|
||||
}
|
||||
<script type="text/javascript">
|
||||
function onCloseMessage(closeElement) {
|
||||
closeElement.closest('.message').style.display = 'none';
|
||||
}
|
||||
|
||||
function onSummaryChange(evt) {
|
||||
const summary = evt.currentTarget.value;
|
||||
const projectId = document.getElementById("pr-project").value;
|
||||
sessionStorage.setItem(`pr-summary-${projectId}`, summary);
|
||||
}
|
||||
|
||||
function onPullRequestTemplateChange(evt) {
|
||||
const prTemplate = evt.currentTarget.value;
|
||||
const pullRequestId = document.getElementById("pr-pullrequest").value;
|
||||
localStorage.setItem(`pr-template-${pullRequestId}`, prTemplate);
|
||||
}
|
||||
|
||||
function savePreferred() {
|
||||
savePreferredProject()
|
||||
savePreferredPullRequest()
|
||||
}
|
||||
|
||||
function savePreferredProject() {
|
||||
const projectId = document.getElementById("pr-project").value;
|
||||
localStorage.setItem(`preferred-project`, projectId);
|
||||
}
|
||||
|
||||
function restorePreferredProject() {
|
||||
const preferredProject = localStorage.getItem(`preferred-project`);
|
||||
if (!preferredProject) return;
|
||||
const projectElement = document.getElementById("pr-project");
|
||||
if (!projectElement) return;
|
||||
if (preferredProject === projectElement.value) return;
|
||||
projectElement.value = preferredProject;
|
||||
projectElement.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
function savePreferredPullRequest() {
|
||||
const pullRequestId = document.getElementById("pr-pullrequest").value;
|
||||
localStorage.setItem(`preferred-pullrequest`, pullRequestId);
|
||||
}
|
||||
|
||||
function restorePreferredPullRequest() {
|
||||
const preferredPullRequest = localStorage.getItem(`preferred-pullrequest`);
|
||||
if (!preferredPullRequest) return;
|
||||
const pullRequestElement = document.getElementById("pr-pullrequest");
|
||||
if (!pullRequestElement) return;
|
||||
if (preferredPullRequest === pullRequestElement.value) return;
|
||||
pullRequestElement.value = preferredPullRequest;
|
||||
pullRequestElement.dispatchEvent(new Event('change'));
|
||||
}
|
||||
|
||||
function restoreLastSummary() {
|
||||
const summaryTextarea = document.getElementById("pr-summary");
|
||||
if (!summaryTextarea) return;
|
||||
const summary = summaryTextarea.value;
|
||||
if (summary !== "") return;
|
||||
const projectId = document.getElementById("pr-project").value;
|
||||
if (!projectId) return;
|
||||
const savedSummary = sessionStorage.getItem(`pr-summary-${projectId}`);
|
||||
if (!savedSummary) return;
|
||||
summaryTextarea.value = savedSummary;
|
||||
}
|
||||
|
||||
function restorePullRequestTemplate() {
|
||||
const prTemplateTextarea = document.getElementById("pr-template");
|
||||
if (!prTemplateTextarea) return;
|
||||
const prTemplate = prTemplateTextarea.value;
|
||||
if (prTemplate !== "") return;
|
||||
const projectId = document.getElementById("pr-project").value;
|
||||
if (!projectId) return;
|
||||
const savedprTemplate = localStorage.getItem(`pr-template-${projectId}`);
|
||||
if (!savedprTemplate) return;
|
||||
prTemplateTextarea.value = savedprTemplate;
|
||||
}
|
||||
|
||||
htmx.onLoad(function(){
|
||||
restoreLastSummary();
|
||||
restorePreferredProject();
|
||||
restorePreferredPullRequest();
|
||||
restorePullRequestTemplate();
|
||||
})
|
||||
</script>
|
||||
}
|
||||
}
|
||||
|
||||
func projectsToOptions(projects []*model.Project) []string {
|
||||
options := make([]string, 0, len(projects))
|
||||
options = append(options, "", "")
|
||||
for _, p := range projects {
|
||||
options = append(options, p.Name, p.ID)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func pullRequestsToOptions(pullRequests []*model.PullRequest) []string {
|
||||
options := make([]string, 0, len(pullRequests))
|
||||
options = append(options, "", "")
|
||||
for _, pr := range pullRequests {
|
||||
options = append(options, pr.Title, pr.ID)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func markdownToHTML(ctx context.Context, text string) string {
|
||||
var buff bytes.Buffer
|
||||
if err := goldmark.Convert([]byte(text), &buff); err != nil {
|
||||
slog.ErrorContext(ctx, "could not convert markdown to html", slog.Any("error", errors.WithStack(err)))
|
||||
return ""
|
||||
}
|
||||
|
||||
return buff.String()
|
||||
}
|
@ -0,0 +1,320 @@
|
||||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.3.819
|
||||
package component
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import templruntime "github.com/a-h/templ/runtime"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/core/model"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/http/form"
|
||||
common "forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/common/component"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/yuin/goldmark"
|
||||
"log/slog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PullRequestPageVModel struct {
|
||||
PullRequestURL string
|
||||
SummaryForm *form.Form
|
||||
PullRequestForm *form.Form
|
||||
PullRequestTips string
|
||||
Projects []*model.Project
|
||||
PullRequests []*model.PullRequest
|
||||
SelectedProjectID string
|
||||
SelectedPullRequestID string
|
||||
}
|
||||
|
||||
const summaryPlaceholder = `
|
||||
Décrivez rapidement les modifications apportées par la PR, ClearCase utilisera le modèle de PR présent dans le dépôt (ou un modèle par défaut) afin de générer une version mise en forme et complétée.
|
||||
|
||||
Afin de fournir plus d'information de contexte au LLM, vous pouvez faire référence à d'autres tickets du dépôt via un ou plusieurs '#<pr_id>' et/ou des chemins vers des fichiers présents dans celui ci.
|
||||
`
|
||||
|
||||
const bodyPlaceholder = `
|
||||
Une fois votre PR générée, vous pourrez l'éditer puis la créer directement en cliquant sur le bouton 'Mettre à jour' ci-dessous.
|
||||
`
|
||||
|
||||
const prTemplatePlaceholder = `
|
||||
Vous pouvez surcharger le modèle de PR fourni par le projet en remplissant ce champ.
|
||||
`
|
||||
|
||||
func NewPullRequestSummaryForm() *form.Form {
|
||||
return form.New(
|
||||
form.NewField(
|
||||
"project",
|
||||
form.Attrs{},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"pullrequest",
|
||||
form.Attrs{},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"summary",
|
||||
form.Attrs{
|
||||
"type": "textarea",
|
||||
"rows": "20",
|
||||
"placeholder": strings.TrimSpace(summaryPlaceholder),
|
||||
},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"template",
|
||||
form.Attrs{
|
||||
"type": "textarea",
|
||||
"rows": "20",
|
||||
"placeholder": strings.TrimSpace(prTemplatePlaceholder),
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func NewPullRequestForm() *form.Form {
|
||||
return form.New(
|
||||
form.NewField(
|
||||
"title",
|
||||
form.Attrs{
|
||||
"type": "text",
|
||||
"placeholder": "Écrivez le résumé de votre demande et cliquez sur 'Générer' pour remplir automatiquement ces champs.",
|
||||
},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
form.NewField(
|
||||
"body",
|
||||
form.Attrs{
|
||||
"type": "textarea",
|
||||
"rows": "20",
|
||||
"placeholder": strings.TrimSpace(bodyPlaceholder),
|
||||
},
|
||||
form.NonEmpty("Ce champ ne doit pas être vide."),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func PullRequestPage(vmodel PullRequestPageVModel) templ.Component {
|
||||
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
|
||||
return templ_7745c5c3_CtxErr
|
||||
}
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
defer func() {
|
||||
templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err == nil {
|
||||
templ_7745c5c3_Err = templ_7745c5c3_BufErr
|
||||
}
|
||||
}()
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
if vmodel.PullRequestURL != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<article class=\"message is-primary\"><div class=\"message-header\"><p>Pull Request modifiée !</p><button class=\"delete\" aria-label=\"delete\" hx-on:click=\"onCloseMessage(this)\"></button></div><div class=\"message-body\">Votre PR a été mise à jour et est disponible à l'adresse suivante: <a href=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 templ.SafeURL = templ.SafeURL(vmodel.PullRequestURL)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var3)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\" target=\"_blank\"><code>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(vmodel.PullRequestURL)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/http/handler/webui/pullrequest/component/pullrequest_page.templ`, Line: 106, Col: 99}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "</code></a>.</div></article><script type=\"text/javascript\">\n\t\t\t\t\t\tfunction clearSummary(projectId) {\n\t\t\t\t\t\t\tsessionStorage.removeItem(`pr-summary-${projectId}`)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfunction openPR(prUrl) {\n\t\t\t\t\t\t\twindow.open(prUrl, \"_blank\");\n\t\t\t\t\t\t}\n\t\t\t\t\t</script> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.JSFuncCall("clearSummary", vmodel.SelectedProjectID).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, " ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.JSFuncCall("openPR", vmodel.PullRequestURL).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 5, " <div class=\"columns\"><div class=\"column is-4\"><form id=\"summary-form\" action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 templ.SafeURL = common.CurrentURL(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var5)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "\" method=\"put\" hx-disabled-elt=\"textarea, input, select, button\" hx-on:htmx:before-send=\"savePreferred()\" hx-indicator=\"#generation-progress\"><h2 class=\"title is-size-3\">Résumé de la PR</h2>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = common.FormSelect(
|
||||
vmodel.SummaryForm, "pr-project", "project", "Projet",
|
||||
common.WithOptions(projectsToOptions(vmodel.Projects)...),
|
||||
common.WithAttrs(
|
||||
"hx-get", string(common.CurrentURL(ctx, common.WithoutValues("project", "*"))),
|
||||
"hx-target", "body",
|
||||
"hx-push-url", "true",
|
||||
),
|
||||
).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = common.FormSelect(
|
||||
vmodel.SummaryForm, "pr-pullrequest", "pullrequest", "PR",
|
||||
common.WithOptions(pullRequestsToOptions(vmodel.PullRequests)...),
|
||||
common.WithAttrs(
|
||||
"hx-get", string(common.CurrentURL(ctx, common.WithoutValues("pullrequest", "*"))),
|
||||
"hx-target", "body",
|
||||
"hx-push-url", "true",
|
||||
),
|
||||
).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = common.FormTextarea(
|
||||
vmodel.SummaryForm, "pr-summary", "summary", "Résumé",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onSummaryChange(event)",
|
||||
),
|
||||
).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "<details class=\"my-3\"><summary class=\"is-clickable\">Paramètres avancés</summary>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = common.FormTextarea(
|
||||
vmodel.SummaryForm, "pr-template", "template", "Surcharger le modèle de demande",
|
||||
common.WithTextareaAttrs(
|
||||
"hx-on:change", "onPullRequestTemplateChange(event)",
|
||||
),
|
||||
).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "</details><div class=\"buttons is-right\"><button type=\"submit\" class=\"button is-info is-large\"><span class=\"icon\"><i class=\"fa fa-robot\"></i></span> <span>Générer</span></button></div></form></div><div class=\"column\"><h2 class=\"title is-size-3\">Votre PR</h2><form action=\"")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 templ.SafeURL = common.CurrentURL(ctx)
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var6)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "\" method=\"post\" hx-disabled-elt=\"textarea, input, select, button\" hx-indicator=\"#generation-progress\">")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = common.FormField(vmodel.PullRequestForm, "pr-title", "title", "Titre").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = common.FormTextarea(vmodel.PullRequestForm, "pr-body", "body", "Corps").Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "<div class=\"buttons is-right\"><button type=\"submit\" class=\"button is-info is-large\"><span class=\"icon\"><i class=\"fa fa-rocket\"></i></span> <span>Mettre à jour</span></button></div></form></div></div><progress id=\"generation-progress\" class=\"htmx-indicator progress\"></progress> ")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if vmodel.PullRequestTips != "" {
|
||||
html := markdownToHTML(ctx, vmodel.PullRequestTips)
|
||||
if html != "" {
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "<article class=\"message is-info mt-5\"><div class=\"message-header\"><p><span class=\"icon\"><i class=\"fa fa-lightbulb\"></i></span>Questionnements</p><button class=\"delete\" aria-label=\"delete\" hx-on:click=\"onCloseMessage(this)\"></button></div><div class=\"message-body\"><div class=\"content\"><p>Utilisez ces quelques questions pour réfléchir aux éléments d'informations nécessaire à la bonne rédaction de votre PR:</p>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.Raw(html).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "</div></div></article>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, " <script type=\"text/javascript\">\n\t\tfunction onCloseMessage(closeElement) {\n\t\t\tcloseElement.closest('.message').style.display = 'none';\n\t\t}\n\n\t\tfunction onSummaryChange(evt) {\n\t\t\tconst summary = evt.currentTarget.value;\n\t\t\tconst projectId = document.getElementById(\"pr-project\").value;\n\t\t\tsessionStorage.setItem(`pr-summary-${projectId}`, summary);\n\t\t}\n\n\t\tfunction onPullRequestTemplateChange(evt) {\n\t\t\tconst prTemplate = evt.currentTarget.value;\n\t\t\tconst pullRequestId = document.getElementById(\"pr-pullrequest\").value;\n\t\t\tlocalStorage.setItem(`pr-template-${pullRequestId}`, prTemplate);\n\t\t}\n\n\t\tfunction savePreferred() {\n\t\t\tsavePreferredProject()\n\t\t\tsavePreferredPullRequest()\n\t\t}\n\n\t\tfunction savePreferredProject() {\n\t\t\tconst projectId = document.getElementById(\"pr-project\").value;\n\t\t\tlocalStorage.setItem(`preferred-project`, projectId);\n\t\t}\n\n\t\tfunction restorePreferredProject() {\n\t\t\tconst preferredProject = localStorage.getItem(`preferred-project`);\n\t\t\tif (!preferredProject) return;\n\t\t\tconst projectElement = document.getElementById(\"pr-project\");\n\t\t\tif (!projectElement) return;\n\t\t\tif (preferredProject === projectElement.value) return;\n\t\t\tprojectElement.value = preferredProject;\n\t\t\tprojectElement.dispatchEvent(new Event('change'));\n\t\t}\n\n\t\tfunction savePreferredPullRequest() {\n\t\t\tconst pullRequestId = document.getElementById(\"pr-pullrequest\").value;\n\t\t\tlocalStorage.setItem(`preferred-pullrequest`, pullRequestId);\n\t\t}\n\n\t\tfunction restorePreferredPullRequest() {\n\t\t\tconst preferredPullRequest = localStorage.getItem(`preferred-pullrequest`);\n\t\t\tif (!preferredPullRequest) return;\n\t\t\tconst pullRequestElement = document.getElementById(\"pr-pullrequest\");\n\t\t\tif (!pullRequestElement) return;\n\t\t\tif (preferredPullRequest === pullRequestElement.value) return;\n\t\t\tpullRequestElement.value = preferredPullRequest;\n\t\t\tpullRequestElement.dispatchEvent(new Event('change'));\n\t\t}\n\n\t\tfunction restoreLastSummary() {\n\t\t\tconst summaryTextarea = document.getElementById(\"pr-summary\");\n\t\t\tif (!summaryTextarea) return;\n const summary = summaryTextarea.value;\n\t\t\tif (summary !== \"\") return;\n\t\t\tconst projectId = document.getElementById(\"pr-project\").value;\n\t\t\tif (!projectId) return;\n\t\t\tconst savedSummary = sessionStorage.getItem(`pr-summary-${projectId}`);\n\t\t\tif (!savedSummary) return;\n\t\t\tsummaryTextarea.value = savedSummary;\n\t\t}\n\n\t\tfunction restorePullRequestTemplate() {\n\t\t\tconst prTemplateTextarea = document.getElementById(\"pr-template\");\n\t\t\tif (!prTemplateTextarea) return;\n const prTemplate = prTemplateTextarea.value;\n\t\t\tif (prTemplate !== \"\") return;\n\t\t\tconst projectId = document.getElementById(\"pr-project\").value;\n\t\t\tif (!projectId) return;\n\t\t\tconst savedprTemplate = localStorage.getItem(`pr-template-${projectId}`);\n\t\t\tif (!savedprTemplate) return;\n\t\t\tprTemplateTextarea.value = savedprTemplate;\n\t\t}\n\n\t\thtmx.onLoad(function(){\n\t\t\trestoreLastSummary();\n\t\t\trestorePreferredProject();\n\t\t\trestorePreferredPullRequest();\n\t\t\trestorePullRequestTemplate();\n })\n\t\t</script>")
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
templ_7745c5c3_Err = common.AppPage(common.WithPageOptions(
|
||||
common.WithTitle("Éditer une PR"),
|
||||
)).Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func projectsToOptions(projects []*model.Project) []string {
|
||||
options := make([]string, 0, len(projects))
|
||||
options = append(options, "", "")
|
||||
for _, p := range projects {
|
||||
options = append(options, p.Name, p.ID)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func pullRequestsToOptions(pullRequests []*model.PullRequest) []string {
|
||||
options := make([]string, 0, len(pullRequests))
|
||||
options = append(options, "", "")
|
||||
for _, pr := range pullRequests {
|
||||
options = append(options, pr.Title, pr.ID)
|
||||
}
|
||||
return options
|
||||
}
|
||||
|
||||
func markdownToHTML(ctx context.Context, text string) string {
|
||||
var buff bytes.Buffer
|
||||
if err := goldmark.Convert([]byte(text), &buff); err != nil {
|
||||
slog.ErrorContext(ctx, "could not convert markdown to html", slog.Any("error", errors.WithStack(err)))
|
||||
return ""
|
||||
}
|
||||
|
||||
return buff.String()
|
||||
}
|
||||
|
||||
var _ = templruntime.GeneratedTemplate
|
46
internal/http/handler/webui/pullrequest/handler.go
Normal file
46
internal/http/handler/webui/pullrequest/handler.go
Normal file
@ -0,0 +1,46 @@
|
||||
package pullrequest
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/core/service"
|
||||
httpCtx "forge.cadoles.com/wpetit/clearcase/internal/http/context"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/common"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/http/url"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
mux *http.ServeMux
|
||||
forge *service.ForgeManager
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.mux.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func NewHandler(forge *service.ForgeManager) *Handler {
|
||||
h := &Handler{
|
||||
mux: http.NewServeMux(),
|
||||
forge: forge,
|
||||
}
|
||||
|
||||
h.mux.HandleFunc("GET /", h.getPullRequestPage)
|
||||
h.mux.HandleFunc("PUT /", h.handlePullRequestSummaryForm)
|
||||
h.mux.HandleFunc("POST /", h.handlePullRequestForm)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *Handler) handleError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
if errors.Is(err, service.ErrForgeNotAvailable) {
|
||||
baseURL := url.Mutate(httpCtx.BaseURL(r.Context()), url.WithPath("/auth/logout"))
|
||||
http.Redirect(w, r, baseURL.String(), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
common.HandleError(w, r, errors.WithStack(err))
|
||||
}
|
||||
|
||||
var _ http.Handler = &Handler{}
|
242
internal/http/handler/webui/pullrequest/pullrequest_page.go
Normal file
242
internal/http/handler/webui/pullrequest/pullrequest_page.go
Normal file
@ -0,0 +1,242 @@
|
||||
package pullrequest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
httpCtx "forge.cadoles.com/wpetit/clearcase/internal/http/context"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/http/form"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/common"
|
||||
"forge.cadoles.com/wpetit/clearcase/internal/http/handler/webui/pullrequest/component"
|
||||
|
||||
"github.com/a-h/templ"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (h *Handler) getPullRequestPage(w http.ResponseWriter, r *http.Request) {
|
||||
vmodel, err := h.fillPullRequestPageVModel(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
pr := component.PullRequestPage(*vmodel)
|
||||
templ.Handler(pr).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *Handler) fillPullRequestPageVModel(r *http.Request) (*component.PullRequestPageVModel, error) {
|
||||
vmodel := &component.PullRequestPageVModel{
|
||||
SummaryForm: component.NewPullRequestSummaryForm(),
|
||||
PullRequestForm: component.NewPullRequestForm(),
|
||||
}
|
||||
|
||||
err := common.FillViewModel(
|
||||
r.Context(), vmodel, r,
|
||||
h.fillPullRequestPageSelectedProject,
|
||||
h.fillPullRequestPagePullRequests,
|
||||
h.fillPullRequestPageProjects,
|
||||
h.fillPullRequestPageSelectedPullRequest,
|
||||
h.fillPullRequestPageSummary,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return vmodel, nil
|
||||
}
|
||||
|
||||
func (h *Handler) fillPullRequestPageProjects(ctx context.Context, vmodel *component.PullRequestPageVModel, r *http.Request) error {
|
||||
user := httpCtx.User(ctx)
|
||||
|
||||
projects, err := h.forge.GetUserProjects(ctx, user)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
vmodel.Projects = projects
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) fillPullRequestPagePullRequests(ctx context.Context, vmodel *component.PullRequestPageVModel, r *http.Request) error {
|
||||
if vmodel.SelectedProjectID == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
user := httpCtx.User(ctx)
|
||||
|
||||
pullRequests, err := h.forge.GetUserProjectOpenedPullRequests(ctx, user, vmodel.SelectedProjectID)
|
||||
if err != nil {
|
||||
return errors.WithStack(err)
|
||||
}
|
||||
|
||||
vmodel.PullRequests = pullRequests
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) fillPullRequestPageSelectedProject(ctx context.Context, vmodel *component.PullRequestPageVModel, r *http.Request) error {
|
||||
project := r.URL.Query().Get("project")
|
||||
if project == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
vmodel.SelectedProjectID = project
|
||||
vmodel.SummaryForm.Field("project").Set("value", project)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) fillPullRequestPageSelectedPullRequest(ctx context.Context, vmodel *component.PullRequestPageVModel, r *http.Request) error {
|
||||
pullRequest := r.URL.Query().Get("pullrequest")
|
||||
if pullRequest == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
vmodel.SelectedPullRequestID = pullRequest
|
||||
vmodel.SummaryForm.Field("pullrequest").Set("value", pullRequest)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) fillPullRequestPageSummary(ctx context.Context, vmodel *component.PullRequestPageVModel, r *http.Request) error {
|
||||
if vmodel.SelectedProjectID == "" || vmodel.SelectedPullRequestID == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
user := httpCtx.User(ctx)
|
||||
|
||||
pullRequest, err := h.forge.GetUserProjectPullRequest(ctx, user, vmodel.SelectedProjectID, vmodel.SelectedPullRequestID)
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "could not retrieve selected pull request", slog.Any("error", errors.WithStack(err)))
|
||||
return nil
|
||||
}
|
||||
|
||||
vmodel.SummaryForm.Field("summary").Set("value", pullRequest.Body)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Handler) handlePullRequestSummaryForm(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
pullRequestSummaryForm := component.NewPullRequestSummaryForm()
|
||||
|
||||
if err := pullRequestSummaryForm.Handle(r); err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
vmodel, err := h.fillPullRequestPageVModel(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
vmodel.SummaryForm = pullRequestSummaryForm
|
||||
|
||||
if errs := pullRequestSummaryForm.Validate(); errs != nil {
|
||||
page := component.PullRequestPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
projectID, err := form.FormFieldAttr[string](pullRequestSummaryForm, "project", "value")
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
pullRequestID, err := form.FormFieldAttr[string](pullRequestSummaryForm, "pullrequest", "value")
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
prSummary, err := form.FormFieldAttr[string](pullRequestSummaryForm, "summary", "value")
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
prTemplate, err := form.FormFieldAttr[string](pullRequestSummaryForm, "template", "value")
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
prTemplate = strings.TrimSpace(prTemplate)
|
||||
|
||||
prTitle, prBody, prTips, err := h.forge.GeneratePullRequest(ctx, httpCtx.User(ctx), projectID, pullRequestID, prSummary, prTemplate)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
vmodel.PullRequestTips = prTips
|
||||
vmodel.PullRequestForm.Field("title").Set("value", prTitle)
|
||||
vmodel.PullRequestForm.Field("body").Set("value", prBody)
|
||||
|
||||
page := component.PullRequestPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (h *Handler) handlePullRequestForm(w http.ResponseWriter, r *http.Request) {
|
||||
pullRequestForm := component.NewPullRequestForm()
|
||||
|
||||
if err := pullRequestForm.Handle(r); err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
vmodel, err := h.fillPullRequestPageVModel(r)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
if errs := pullRequestForm.Validate(); errs != nil {
|
||||
vmodel.PullRequestForm = pullRequestForm
|
||||
|
||||
page := component.PullRequestPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
projectID := r.URL.Query().Get("project")
|
||||
pullRequestID := r.URL.Query().Get("pullrequest")
|
||||
|
||||
title, err := form.FormFieldAttr[string](pullRequestForm, "title", "value")
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
body, err := form.FormFieldAttr[string](pullRequestForm, "body", "value")
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
user := httpCtx.User(ctx)
|
||||
|
||||
issueURL, err := h.forge.UpdatePullRequest(ctx, user, projectID, pullRequestID, title, body)
|
||||
if err != nil {
|
||||
h.handleError(w, r, errors.WithStack(err))
|
||||
return
|
||||
}
|
||||
|
||||
vmodel.PullRequestURL = issueURL
|
||||
|
||||
vmodel.SummaryForm.Field("summary").Set("value", "")
|
||||
vmodel.PullRequestForm.Field("title").Set("value", "")
|
||||
vmodel.PullRequestForm.Field("body").Set("value", "")
|
||||
|
||||
page := component.PullRequestPage(*vmodel)
|
||||
templ.Handler(page).ServeHTTP(w, r)
|
||||
}
|
Reference in New Issue
Block a user