rework list courses page to be flatten
This commit is contained in:
@ -1 +1 @@
|
|||||||
8d0ebfae489db7ae534fee52ed6ceb3f
|
c2a78909343b26a169423b19ada40fad
|
||||||
|
|||||||
@ -29,7 +29,13 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const savingOrganizationIDInternalInsteadOfExternal = false
|
||||||
|
|
||||||
func app(ctx context.Context) error {
|
func app(ctx context.Context) error {
|
||||||
|
if !savingOrganizationIDInternalInsteadOfExternal {
|
||||||
|
panic("fix saving ogranization id as external id instead of internal")
|
||||||
|
}
|
||||||
|
|
||||||
var cfgpath string
|
var cfgpath string
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
cfgpath = os.Args[1]
|
cfgpath = os.Args[1]
|
||||||
|
|||||||
@ -107,11 +107,21 @@ func middlewareCustomWriterInjector() mux.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type attributeStringKey string
|
||||||
|
|
||||||
|
func (k attributeStringKey) Value(value string) attribute.KeyValue {
|
||||||
|
return attribute.String(string(k), value)
|
||||||
|
}
|
||||||
|
|
||||||
func middlewareTrace() mux.MiddlewareFunc {
|
func middlewareTrace() mux.MiddlewareFunc {
|
||||||
reqidAttr := attribute.Key("http.request_id")
|
methodAttr := attributeStringKey("http.request.method")
|
||||||
statusAttr := attribute.Key("http.status_code")
|
reqidAttr := attributeStringKey("http.request_id")
|
||||||
payloadAttr := attribute.Key("http.payload_size")
|
routeAttr := attributeStringKey("http.route")
|
||||||
pathAttr := attribute.Key("http.template_path")
|
queryAttr := attributeStringKey("url.query")
|
||||||
|
pathAttr := attributeStringKey("http.path")
|
||||||
|
uaAttr := attributeStringKey("user_agent.original")
|
||||||
|
statusAttr := attribute.Key("http.response.status_code")
|
||||||
|
payloadAttr := attribute.Key("http.response_content_length")
|
||||||
|
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -120,14 +130,18 @@ func middlewareTrace() mux.MiddlewareFunc {
|
|||||||
|
|
||||||
var span trace.Span
|
var span trace.Span
|
||||||
route := mux.CurrentRoute(r)
|
route := mux.CurrentRoute(r)
|
||||||
hname := route.GetName()
|
|
||||||
hpath, _ := route.GetPathTemplate()
|
hpath, _ := route.GetPathTemplate()
|
||||||
ctx, span = webtracer.Start(
|
ctx, span = webtracer.Start(
|
||||||
ctx, "http."+hname,
|
ctx, r.Method+" "+hpath,
|
||||||
trace.WithAttributes(
|
trace.WithAttributes(
|
||||||
reqidAttr.String(reqid),
|
methodAttr.Value(r.Method),
|
||||||
pathAttr.String(hpath),
|
reqidAttr.Value(reqid),
|
||||||
|
routeAttr.Value(hpath),
|
||||||
|
pathAttr.Value(r.URL.Path),
|
||||||
|
queryAttr.Value(r.URL.RawQuery),
|
||||||
|
uaAttr.Value(r.UserAgent()),
|
||||||
),
|
),
|
||||||
|
trace.WithSpanKind(trace.SpanKindServer),
|
||||||
)
|
)
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
|
|||||||
@ -8,18 +8,19 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.loyso.art/frx/kurious/internal/common/config"
|
"git.loyso.art/frx/kurious/internal/common/config"
|
||||||
"git.loyso.art/frx/kurious/pkg/xdefault"
|
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
|
||||||
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
|
||||||
|
"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
|
||||||
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||||
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||||
"go.opentelemetry.io/otel/propagation"
|
"go.opentelemetry.io/otel/propagation"
|
||||||
"go.opentelemetry.io/otel/sdk/metric"
|
"go.opentelemetry.io/otel/sdk/metric"
|
||||||
"go.opentelemetry.io/otel/sdk/resource"
|
"go.opentelemetry.io/otel/sdk/resource"
|
||||||
"go.opentelemetry.io/otel/sdk/trace"
|
"go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.24.0"
|
||||||
)
|
)
|
||||||
|
|
||||||
var webtracer = otel.Tracer("kuriweb.http")
|
var webtracer = otel.Tracer("kuriweb.http")
|
||||||
@ -46,19 +47,26 @@ func setupOtelSDK(ctx context.Context, cfg config.Trace) (shutdown shutdownFunc,
|
|||||||
prop := newPropagator()
|
prop := newPropagator()
|
||||||
otel.SetTextMapPropagator(prop)
|
otel.SetTextMapPropagator(prop)
|
||||||
|
|
||||||
tracerProvider, err := newTraceProvider(ctx, cfg.Endpoint, cfg.LicenseKey)
|
tracerProvider, err := newCommonTraceProvider(ctx, TraceProviderParams{
|
||||||
|
Endpoint: cfg.Endpoint,
|
||||||
|
Type: cfg.Type,
|
||||||
|
AuthHeader: cfg.APIHeader,
|
||||||
|
APIKey: cfg.APIKey,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, handleError(err)
|
return nil, handleError(err)
|
||||||
}
|
}
|
||||||
shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
|
shutdownFuncs = append(shutdownFuncs, tracerProvider.Shutdown)
|
||||||
otel.SetTracerProvider(tracerProvider)
|
otel.SetTracerProvider(tracerProvider)
|
||||||
|
|
||||||
|
if cfg.ShowMetrics {
|
||||||
meterProvider, err := newMeterProvider()
|
meterProvider, err := newMeterProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, handleError(err)
|
return nil, handleError(err)
|
||||||
}
|
}
|
||||||
shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
|
shutdownFuncs = append(shutdownFuncs, meterProvider.Shutdown)
|
||||||
otel.SetMeterProvider(meterProvider)
|
otel.SetMeterProvider(meterProvider)
|
||||||
|
}
|
||||||
|
|
||||||
return shutdown, nil
|
return shutdown, nil
|
||||||
}
|
}
|
||||||
@ -70,46 +78,75 @@ func newPropagator() propagation.TextMapPropagator {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultNewRelicEndpoint = "otlp.eu01.nr-data.net:443"
|
type TraceProviderParams struct {
|
||||||
|
Type config.TraceClientType
|
||||||
|
Endpoint string
|
||||||
|
APIKey string
|
||||||
|
AuthHeader string
|
||||||
|
}
|
||||||
|
|
||||||
func newTraceProvider(ctx context.Context, endpoint, licensekey string) (traceProvider *trace.TracerProvider, err error) {
|
func newCommonTraceProvider(ctx context.Context, params TraceProviderParams) (tp *trace.TracerProvider, err error) {
|
||||||
opts := make([]trace.TracerProviderOption, 0, 2)
|
r, err := resource.New(
|
||||||
|
ctx,
|
||||||
|
resource.WithDetectors(
|
||||||
|
resource.StringDetector(semconv.SchemaURL, semconv.ServiceNameKey, func() (string, error) {
|
||||||
|
return "bigstats:kuriweb", nil
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("making new resource: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err = resource.Merge(resource.Default(), r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("merging resources: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := make([]trace.TracerProviderOption, 0, 4)
|
||||||
opts = append(
|
opts = append(
|
||||||
opts,
|
opts,
|
||||||
trace.WithSampler(trace.AlwaysSample()),
|
trace.WithSampler(trace.AlwaysSample()),
|
||||||
trace.WithResource(resource.Default()),
|
trace.WithResource(r),
|
||||||
)
|
)
|
||||||
|
|
||||||
if licensekey != "" {
|
if params.Type != config.TraceClientTypeUnset {
|
||||||
endpoint = xdefault.WithFallback(endpoint, defaultNewRelicEndpoint)
|
var spanExporter trace.SpanExporter
|
||||||
client, err := otlptracegrpc.New(
|
var headers map[string]string
|
||||||
|
|
||||||
|
if params.AuthHeader != "" {
|
||||||
|
headers = make(map[string]string, 1)
|
||||||
|
headers[params.AuthHeader] = params.APIKey
|
||||||
|
}
|
||||||
|
|
||||||
|
switch params.Type {
|
||||||
|
case config.TraceClientTypeGRPC:
|
||||||
|
spanExporter, err = otlptracegrpc.New(
|
||||||
ctx,
|
ctx,
|
||||||
otlptracegrpc.WithEndpoint(endpoint),
|
otlptracegrpc.WithEndpointURL(params.Endpoint),
|
||||||
otlptracegrpc.WithHeaders(map[string]string{
|
otlptracegrpc.WithHeaders(headers),
|
||||||
"api-key": licensekey,
|
|
||||||
}),
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
case config.TraceClientTypeHTTP:
|
||||||
return nil, fmt.Errorf("making grpc client: %w", err)
|
httpClient := otlptracehttp.NewClient(
|
||||||
}
|
otlptracehttp.WithEndpointURL(params.Endpoint),
|
||||||
|
otlptracehttp.WithHeaders(headers),
|
||||||
opts = append(opts, trace.WithBatcher(client, trace.WithBatchTimeout(time.Second*10)))
|
|
||||||
} else {
|
|
||||||
traceExporter, err := stdouttrace.New(
|
|
||||||
stdouttrace.WithPrettyPrint())
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
opts = append(
|
|
||||||
opts,
|
|
||||||
trace.WithBatcher(traceExporter, trace.WithBatchTimeout(time.Second*5)),
|
|
||||||
)
|
)
|
||||||
|
spanExporter, err = otlptrace.New(ctx, httpClient)
|
||||||
|
case config.TraceClientTypeStdout:
|
||||||
|
spanExporter, err = stdouttrace.New(stdouttrace.WithPrettyPrint())
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unsupported provider type")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("making trace exporter: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
traceProvider = trace.NewTracerProvider(opts...)
|
opts = append(opts, trace.WithBatcher(spanExporter))
|
||||||
|
}
|
||||||
|
|
||||||
return traceProvider, nil
|
tp = trace.NewTracerProvider(opts...)
|
||||||
|
|
||||||
|
return tp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMeterProvider() (*metric.MeterProvider, error) {
|
func newMeterProvider() (*metric.MeterProvider, error) {
|
||||||
@ -127,6 +164,6 @@ func newMeterProvider() (*metric.MeterProvider, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func muxHandleFunc(router *mux.Router, name, path string, hf http.HandlerFunc) *mux.Route {
|
func muxHandleFunc(router *mux.Router, name, path string, hf http.HandlerFunc) *mux.Route {
|
||||||
h := otelhttp.WithRouteTag(path, hf)
|
// h := otelhttp.WithRouteTag(path, hf)
|
||||||
return router.Handle(path, h).Name(name)
|
return router.Handle(path, hf).Name(name)
|
||||||
}
|
}
|
||||||
|
|||||||
26
go.mod
26
go.mod
@ -12,15 +12,16 @@ require (
|
|||||||
github.com/teris-io/cli v1.0.1
|
github.com/teris-io/cli v1.0.1
|
||||||
github.com/ydb-platform/ydb-go-sdk/v3 v3.54.2
|
github.com/ydb-platform/ydb-go-sdk/v3 v3.54.2
|
||||||
github.com/ydb-platform/ydb-go-yc v0.12.1
|
github.com/ydb-platform/ydb-go-yc v0.12.1
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0
|
go.opentelemetry.io/otel v1.25.0
|
||||||
go.opentelemetry.io/otel v1.24.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.24.0
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.24.0
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0
|
go.opentelemetry.io/otel/sdk v1.25.0
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.24.0
|
go.opentelemetry.io/otel/sdk/metric v1.24.0
|
||||||
go.opentelemetry.io/otel/trace v1.24.0
|
go.opentelemetry.io/otel/trace v1.25.0
|
||||||
golang.org/x/net v0.22.0
|
golang.org/x/net v0.24.0
|
||||||
golang.org/x/sync v0.6.0
|
golang.org/x/sync v0.6.0
|
||||||
golang.org/x/time v0.5.0
|
golang.org/x/time v0.5.0
|
||||||
modernc.org/sqlite v1.29.3
|
modernc.org/sqlite v1.29.3
|
||||||
@ -30,11 +31,9 @@ require (
|
|||||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // indirect
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
@ -47,14 +46,13 @@ require (
|
|||||||
github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162 // indirect
|
github.com/yandex-cloud/go-genproto v0.0.0-20231120081503-a21e9fe75162 // indirect
|
||||||
github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd // indirect
|
github.com/ydb-platform/ydb-go-genproto v0.0.0-20231012155159-f85a672542fd // indirect
|
||||||
github.com/ydb-platform/ydb-go-yc-metadata v0.6.1 // indirect
|
github.com/ydb-platform/ydb-go-yc-metadata v0.6.1 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 // indirect
|
go.opentelemetry.io/otel/metric v1.25.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.1.0 // indirect
|
golang.org/x/sys v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.18.0 // indirect
|
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be // indirect
|
||||||
google.golang.org/grpc v1.62.1 // indirect
|
google.golang.org/grpc v1.63.2 // indirect
|
||||||
google.golang.org/protobuf v1.33.0 // indirect
|
google.golang.org/protobuf v1.33.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
|
|||||||
52
go.sum
52
go.sum
@ -575,8 +575,6 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||||||
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
@ -641,8 +639,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
|||||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
@ -846,31 +842,31 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
|
||||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM=
|
||||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0 h1:t6wl9SPayj+c7lEIFgm4ooDBZVb01IhLB4InpomhRw8=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0/go.mod h1:iSDOcsnSA5INXzZtwaBPrKp/lWu/V14Dd+llD0oI2EA=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0 h1:Mw5xcxMwlqoJd97vwPxA8isEaIoxsta9/Q51+TTJLGE=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0/go.mod h1:CQNu9bj7o7mC6U7+CA/schKEYakYXWr79ucDHTMGhCM=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA=
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.24.0 h1:JYE2HM7pZbOt5Jhk8ndWZTUWYOVift2cHjXVMkPdmdc=
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.24.0 h1:JYE2HM7pZbOt5Jhk8ndWZTUWYOVift2cHjXVMkPdmdc=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.24.0/go.mod h1:yMb/8c6hVsnma0RpsBMNo0fEiQKeclawtgaIaOp2MLY=
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.24.0/go.mod h1:yMb/8c6hVsnma0RpsBMNo0fEiQKeclawtgaIaOp2MLY=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0 h1:s0PHtIkN+3xrbDOpt2M8OTG92cWqUESvzh2MxiR5xY8=
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.24.0/go.mod h1:hZlFbDbRt++MMPCCfSJfmhkGIWnX1h3XjkfxZUjLrIA=
|
||||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
|
||||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
|
||||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8=
|
go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0=
|
go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
|
||||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||||
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U=
|
||||||
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
|
go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
|
||||||
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
|
go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
@ -1000,8 +996,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -1126,8 +1122,8 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
@ -1430,10 +1426,10 @@ google.golang.org/genproto v0.0.0-20230209215440-0dfe4f8abfcc/go.mod h1:RGgjbofJ
|
|||||||
google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA=
|
google.golang.org/genproto v0.0.0-20230216225411-c8e22ba71e44/go.mod h1:8B0gmkoRebU8ukX6HP+4wrVQUY1+6PkQ44BSyIlflHA=
|
||||||
google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
|
google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec/go.mod h1:3Dl5ZL0q0isWJt+FVcfpQyirqemEuLAK/iFvg1UP1Hw=
|
||||||
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4/go.mod h1:NWraEVixdDnqcqQ30jipen1STv2r/n24Wb7twVTGR4s=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda h1:b6F6WIV4xHHD0FA4oIyzU6mHWg2WI2X1RBehwa5QN38=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be h1:Zz7rLWqp0ApfsR/l7+zSHhY3PMiH2xqgxlfYfAfNpoU=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda/go.mod h1:AHcE/gZH76Bk/ROZhQphlRoWo5xKDEtz3eVEO1LfA8c=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240415180920-8c6c420018be/go.mod h1:dvdCTIoAGbkWbcIKBniID56/7XHTt6WfxXNMxuziJ+w=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be h1:LG9vZxsWGOmUKieR8wPAUR3u3MpnYFQZROPIMaXh7/A=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240415180920-8c6c420018be/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
@ -1474,8 +1470,8 @@ google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsA
|
|||||||
google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
|
||||||
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
|
||||||
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
|
||||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
|
|||||||
@ -1,34 +1,53 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
|
<head>
|
||||||
<head>
|
|
||||||
<title>Test page</title>
|
<title>Test page</title>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet"
|
<link
|
||||||
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous" />
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css"
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
rel="stylesheet"
|
||||||
|
integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
/>
|
||||||
|
<script
|
||||||
|
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"
|
||||||
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
|
integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL"
|
||||||
crossorigin="anonymous"></script>
|
crossorigin="anonymous"
|
||||||
|
></script>
|
||||||
<link rel="stylesheet" href="/assets/style.css" />
|
<link rel="stylesheet" href="/assets/style.css" />
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-bs-theme="dark" style="margin: 0">
|
<body data-bs-theme="dark">
|
||||||
<header>
|
<header>
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary w-auto">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary w-auto">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<a class="navbar-brand" href="/index.html">Kurious</a>
|
<a class="navbar-brand" href="/index.html">Kurious</a>
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent"
|
<button
|
||||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
class="navbar-toggler"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="collapse"
|
||||||
|
data-bs-target="#navbarSupportedContent"
|
||||||
|
aria-controls="navbarSupportedContent"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-label="Toggle navigation"
|
||||||
|
>
|
||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="navbar-collapse collapse" id="navbarSupportedContent">
|
<div class="navbar-collapse collapse" id="navbarSupportedContent">
|
||||||
<ul class="navbar-nav mb-lg-0 mb-2 me-auto">
|
<ul class="navbar-nav mb-lg-0 mb-2 me-auto">
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" aria-current="page" href="/index.html">Home</a>
|
<a class="nav-link" aria-current="page" href="/index.html"
|
||||||
|
>Home</a
|
||||||
|
>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link active" aria-current="page" href="/courses.html">Courses</a>
|
<a
|
||||||
|
class="nav-link active"
|
||||||
|
aria-current="page"
|
||||||
|
href="/courses.html"
|
||||||
|
>Courses</a
|
||||||
|
>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="/core.html">About us</a>
|
<a class="nav-link" href="/core.html">About us</a>
|
||||||
@ -41,7 +60,11 @@
|
|||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<section class="row header">
|
<section class="row header">
|
||||||
<nav class="mt-4" style="--bs-breadcrumb-divider: '/'" aria-label="breadcrumb">
|
<nav
|
||||||
|
class="mt-4"
|
||||||
|
style="--bs-breadcrumb-divider: ">""
|
||||||
|
aria-label="breadcrumb"
|
||||||
|
>
|
||||||
<ol class="breadcrumb">
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="#">Main</a></li>
|
<li class="breadcrumb-item"><a href="#">Main</a></li>
|
||||||
<li class="breadcrumb-item" aria-current="page">
|
<li class="breadcrumb-item" aria-current="page">
|
||||||
@ -57,14 +80,22 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text">Filter categories</span>
|
<span class="input-group-text">Filter categories</span>
|
||||||
|
|
||||||
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
|
<select
|
||||||
|
class="form-select"
|
||||||
|
id="inputGroupSelect04"
|
||||||
|
aria-label="Example select with button addon"
|
||||||
|
>
|
||||||
<option selected>All</option>
|
<option selected>All</option>
|
||||||
<option value="1">Programming</option>
|
<option value="1">Programming</option>
|
||||||
<option value="2">Design</option>
|
<option value="2">Design</option>
|
||||||
<option value="3">Business</option>
|
<option value="3">Business</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
|
<select
|
||||||
|
class="form-select"
|
||||||
|
id="inputGroupSelect04"
|
||||||
|
aria-label="Example select with button addon"
|
||||||
|
>
|
||||||
<option selected>All</option>
|
<option selected>All</option>
|
||||||
<option value="1">Web development</option>
|
<option value="1">Web development</option>
|
||||||
<option value="2">Backend</option>
|
<option value="2">Backend</option>
|
||||||
@ -77,50 +108,107 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="row first-class-group">
|
|
||||||
<h1 class="title">Languages</h1>
|
|
||||||
<p>A languages category provides all courses to help learn language</p>
|
|
||||||
|
|
||||||
<div class="filter-content d-flex mb-3">
|
<div class="filter-content d-flex mb-3">
|
||||||
<div class="p-2">
|
<!-- School list -->
|
||||||
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
|
<div class="p-2 col-auto">
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
id="inputGroupSelect04"
|
||||||
|
aria-label="Example select with button addon"
|
||||||
|
>
|
||||||
<option selected>Pick a school</option>
|
<option selected>Pick a school</option>
|
||||||
<option value="1">First school in the row</option>
|
<option value="1">First school in the row</option>
|
||||||
<option value="2">
|
<option value="2">Second but not the shortest named school</option>
|
||||||
Second but not the shortest named school
|
|
||||||
</option>
|
|
||||||
<option value="3">Third small</option>
|
<option value="3">Third small</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Sort option -->
|
||||||
<div class="p-2">
|
<div class="col-auto">
|
||||||
<select class="form-select" id="inputGroupSelect04" aria-label="Example select with button addon">
|
<div class="input-group flex-nowrap p-2">
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
id="inputGroupSelect04"
|
||||||
|
aria-label="Example select with button addon"
|
||||||
|
>
|
||||||
<option selected>Sort by</option>
|
<option selected>Sort by</option>
|
||||||
<option value="1">One</option>
|
<option value="1">One</option>
|
||||||
<option value="2">Two</option>
|
<option value="2">Two</option>
|
||||||
<option value="3">Three</option>
|
<option value="3">Three</option>
|
||||||
<option value="4">Threerrrrrrrrrrrrrrrrrr</option>
|
<option value="4">Threerrrrrrrrrrrrrrrrrr</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="ms-auto p-2">
|
<input
|
||||||
|
type="radio"
|
||||||
|
class="btn-check p-2"
|
||||||
|
name="options-base"
|
||||||
|
id="option6"
|
||||||
|
autocomplete="off"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
<label class="btn" for="option6">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="bi bi-sort-down"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.5 2.5a.5.5 0 0 0-1 0v8.793l-1.146-1.147a.5.5 0 0 0-.708.708l2 1.999.007.007a.497.497 0 0 0 .7-.006l2-2a.5.5 0 0 0-.707-.708L3.5 11.293zm3.5 1a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M7.5 6a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
class="btn-check p-2"
|
||||||
|
name="options-base"
|
||||||
|
id="option5"
|
||||||
|
autocomplete="off"
|
||||||
|
checked
|
||||||
|
/>
|
||||||
|
<label class="btn" for="option5">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="bi bi-sort-up"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.5 12.5a.5.5 0 0 1-1 0V3.707L1.354 4.854a.5.5 0 1 1-.708-.708l2-1.999.007-.007a.5.5 0 0 1 .7.006l2 2a.5.5 0 1 1-.707.708L3.5 3.707zm3.5-9a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M7.5 6a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Spacer -->
|
||||||
|
<div class="col-4"></div>
|
||||||
|
<!-- Promocodes button -->
|
||||||
|
<div class="col-auto ms-auto p-2">
|
||||||
<div class="btn btn-primary">Promocodes</div>
|
<div class="btn btn-primary">Promocodes</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="second-class-group block">
|
|
||||||
<h2 class="title">Japanese</h2>
|
|
||||||
<p>Looking for a course to learn japanese language?</p>
|
|
||||||
|
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-12 col-md-6 col-lg-3">
|
<div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<img src="https://placehold.co/128x128" class="card-img-top" alt="..." />
|
<img
|
||||||
|
src="https://placehold.co/128x128"
|
||||||
|
class="card-img-top"
|
||||||
|
alt="..."
|
||||||
|
/>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">Card title with a long naming</h5>
|
<h5 class="card-title">Card title with a long naming</h5>
|
||||||
<div class="input-group d-flex">
|
<div class="input-group d-flex">
|
||||||
<a href="#" class="btn text btn-outline-primary flex-grow-1">Open ></a>
|
<a href="#" class="btn text btn-outline-primary flex-grow-1"
|
||||||
<span class="input-group-text justify-content-end flex-fill">500$</span>
|
>Open ></a
|
||||||
|
>
|
||||||
|
<span class="input-group-text justify-content-end flex-fill"
|
||||||
|
>500$</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -128,15 +216,62 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr />
|
<!-- Maybe add spacing in a better way? -->
|
||||||
|
<p class="p-2"></p>
|
||||||
|
<footer class="text-center text-lg-start bg-body-tertiary text-muted">
|
||||||
|
<section class="p-2">
|
||||||
|
<div class="container text-center text-md-start mt-4">
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-md-3 col-lg-4 col-xl-3 mx-auto mb-4">
|
||||||
|
<h6 class="text-uppercase fw-bold mb-4">
|
||||||
|
<i class="fas fa-gem me-3"></i>Courses
|
||||||
|
</h6>
|
||||||
|
<p>
|
||||||
|
Welcome to Courses, your gateway to learning! Explore a diverse
|
||||||
|
range of courses and advance your skills with us. Join our
|
||||||
|
community and transform your life through education.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-3 col-lg-2 col-xl-2 mx-auto mb-4">
|
||||||
|
<h6 class="text-uppercase fw-bold mb-4">Useful links</h6>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Pricing</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Settings</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Orders</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Help</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4 col-lg-3 col-xl-3 mx-auto mb-md-0 mb-4">
|
||||||
|
<h6 class="text-uppercase fw-bold mb-4">Contact</h6>
|
||||||
|
<p><i class="fas fa-home me-3"></i> New York, NY 10012, US</p>
|
||||||
|
<p>
|
||||||
|
<i class="fas fa-envelope me-3"></i>
|
||||||
|
info@example.com
|
||||||
|
</p>
|
||||||
|
<p><i class="fas fa-phone me-3"></i> + 01 234 567 88</p>
|
||||||
|
<p><i class="fas fa-print me-3"></i> + 01 234 567 89</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<footer class="row">
|
<div
|
||||||
<div class="text-end">
|
class="text-center p-4"
|
||||||
<p>(c) All right reserved</p>
|
style="background-color: rgba(0, 0, 0, 0.05)"
|
||||||
|
>
|
||||||
|
© 2024 Copyright:
|
||||||
|
<a class="text-reset fw-bold" href="https://mdbootstrap.com/"
|
||||||
|
>kursov.net</a
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</body>
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,6 +1,38 @@
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
type TraceClientType uint8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TraceClientTypeUnset TraceClientType = iota
|
||||||
|
TraceClientTypeHTTP
|
||||||
|
TraceClientTypeGRPC
|
||||||
|
TraceClientTypeStdout
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t *TraceClientType) UnmarshalText(data []byte) error {
|
||||||
|
dataStr := string(data)
|
||||||
|
switch dataStr {
|
||||||
|
case "http":
|
||||||
|
*t = TraceClientTypeHTTP
|
||||||
|
case "grpc":
|
||||||
|
*t = TraceClientTypeGRPC
|
||||||
|
case "stdout":
|
||||||
|
*t = TraceClientTypeStdout
|
||||||
|
case "":
|
||||||
|
default:
|
||||||
|
return errors.New("unsupported value " + dataStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type Trace struct {
|
type Trace struct {
|
||||||
Endpoint string `json:"endpoint"`
|
Endpoint string `json:"endpoint"`
|
||||||
LicenseKey string `json:"license_key"`
|
APIKey string `json:"api_key"`
|
||||||
|
APIHeader string `json:"api_header"`
|
||||||
|
Type TraceClientType `json:"type"`
|
||||||
|
|
||||||
|
ShowMetrics bool `json:"show_metrics"`
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,11 +16,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
commandAttribute = attribute.Key("command_name")
|
nameAttribute = attribute.Key("cq.name")
|
||||||
queryAttribute = attribute.Key("query_name")
|
argsAttribute = attribute.Key("cq.args")
|
||||||
argsAttribute = attribute.Key("args")
|
|
||||||
|
|
||||||
apiTracer = otel.Tracer("cq")
|
apiTracer = otel.Tracer("api")
|
||||||
)
|
)
|
||||||
|
|
||||||
type commandLoggingDecorator[T any] struct {
|
type commandLoggingDecorator[T any] struct {
|
||||||
@ -37,9 +36,9 @@ func (c commandLoggingDecorator[T]) Handle(ctx context.Context, cmd T) (err erro
|
|||||||
_ = json.NewEncoder(&argsBuilder).Encode(cmd)
|
_ = json.NewEncoder(&argsBuilder).Encode(cmd)
|
||||||
|
|
||||||
var span trace.Span
|
var span trace.Span
|
||||||
ctx, span = apiTracer.Start(ctx, handlerName)
|
ctx, span = apiTracer.Start(ctx, "command "+handlerName)
|
||||||
span.SetAttributes(
|
span.SetAttributes(
|
||||||
commandAttribute.String(handlerName),
|
nameAttribute.String(handlerName),
|
||||||
argsAttribute.String(argsBuilder.String()),
|
argsAttribute.String(argsBuilder.String()),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -73,9 +72,9 @@ func (q queryLoggingDecorator[Q, U]) Handle(ctx context.Context, query Q) (entit
|
|||||||
_ = json.NewEncoder(&argsBuilder).Encode(query)
|
_ = json.NewEncoder(&argsBuilder).Encode(query)
|
||||||
|
|
||||||
var span trace.Span
|
var span trace.Span
|
||||||
ctx, span = apiTracer.Start(ctx, handlerName)
|
ctx, span = apiTracer.Start(ctx, "query "+handlerName)
|
||||||
span.SetAttributes(
|
span.SetAttributes(
|
||||||
queryAttribute.String(handlerName),
|
nameAttribute.String(handlerName),
|
||||||
argsAttribute.String(argsBuilder.String()),
|
argsAttribute.String(argsBuilder.String()),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,21 @@
|
|||||||
package adapters
|
package adapters
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
)
|
||||||
|
|
||||||
|
var dbTracer = otel.Tracer("adapters.db")
|
||||||
|
|
||||||
|
var (
|
||||||
|
dbSystemAttr = attribute.Key("db.system")
|
||||||
|
dbNameAttr = attribute.Key("db.name")
|
||||||
|
dbStatementAttr = attribute.Key("db.statement")
|
||||||
|
dbOperationAttr = attribute.Key("db.operation")
|
||||||
|
dbTableAttr = attribute.Key("db.sql.table")
|
||||||
|
)
|
||||||
|
|
||||||
type domainer[T any] interface {
|
type domainer[T any] interface {
|
||||||
AsDomain() T
|
AsDomain() T
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.loyso.art/frx/kurious/internal/common/xslices"
|
||||||
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,19 +30,26 @@ func (m *inMemoryMapper) CollectCounts(ctx context.Context, cr domain.CourseRepo
|
|||||||
m.stats = map[string]domain.LearningTypeStat{}
|
m.stats = map[string]domain.LearningTypeStat{}
|
||||||
m.courseThematicByLearningType = map[string]string{}
|
m.courseThematicByLearningType = map[string]string{}
|
||||||
|
|
||||||
var nextPageToken string
|
var offset int
|
||||||
for {
|
for {
|
||||||
result, err := cr.List(ctx, domain.ListCoursesParams{
|
result, err := cr.List(ctx, domain.ListCoursesParams{
|
||||||
LearningType: "",
|
LearningType: "",
|
||||||
CourseThematic: "",
|
CourseThematic: "",
|
||||||
NextPageToken: nextPageToken,
|
Limit: batchSize + 1,
|
||||||
Limit: batchSize,
|
Offset: offset,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("listing courses: %w", err)
|
return fmt.Errorf("listing courses: %w", err)
|
||||||
}
|
}
|
||||||
m.totalCount += len(result.Courses)
|
|
||||||
for _, course := range result.Courses {
|
courses := result.Courses
|
||||||
|
if len(result.Courses) >= batchSize {
|
||||||
|
courses = result.Courses[:batchSize]
|
||||||
|
}
|
||||||
|
|
||||||
|
m.totalCount += len(courses)
|
||||||
|
|
||||||
|
xslices.ForEach(courses, func(course domain.Course) {
|
||||||
stat, ok := m.stats[course.LearningTypeID]
|
stat, ok := m.stats[course.LearningTypeID]
|
||||||
stat.Count++
|
stat.Count++
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -50,11 +58,12 @@ func (m *inMemoryMapper) CollectCounts(ctx context.Context, cr domain.CourseRepo
|
|||||||
stat.CourseThematic[course.ThematicID]++
|
stat.CourseThematic[course.ThematicID]++
|
||||||
m.stats[course.LearningTypeID] = stat
|
m.stats[course.LearningTypeID] = stat
|
||||||
m.courseThematicByLearningType[course.ThematicID] = course.LearningTypeID
|
m.courseThematicByLearningType[course.ThematicID] = course.LearningTypeID
|
||||||
}
|
})
|
||||||
if len(result.Courses) < batchSize {
|
|
||||||
|
if len(result.Courses) != batchSize+1 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
nextPageToken = result.NextPageToken
|
offset += batchSize
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
54
internal/kurious/adapters/sqlite_connection.go
Normal file
54
internal/kurious/adapters/sqlite_connection.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package adapters
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.loyso.art/frx/kurious/internal/common/config"
|
||||||
|
"git.loyso.art/frx/kurious/migrations/sqlite"
|
||||||
|
"git.loyso.art/frx/kurious/pkg/xdefault"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
|
_ "modernc.org/sqlite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type sqliteConnection struct {
|
||||||
|
db *sqlx.DB
|
||||||
|
shutdownTimeout time.Duration
|
||||||
|
log *slog.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteConnection(ctx context.Context, cfg config.Sqlite, log *slog.Logger) (*sqliteConnection, error) {
|
||||||
|
conn, err := sqlx.Open("sqlite", cfg.DSN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("openning db connection: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sqlite.RunMigrations(ctx, conn.DB, log)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("running migrations: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sqliteConnection{
|
||||||
|
db: conn,
|
||||||
|
log: log,
|
||||||
|
shutdownTimeout: xdefault.WithFallback(cfg.ShutdownTimeout, defaultShutdownTimeout),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *sqliteConnection) Close() error {
|
||||||
|
_, cancel := context.WithTimeout(context.Background(), c.shutdownTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
return c.db.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSqliteBaseAttributes() []attribute.KeyValue {
|
||||||
|
return []attribute.KeyValue{
|
||||||
|
dbSystemAttr.String("sqlite"),
|
||||||
|
dbNameAttr.String("courses"),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,52 +9,15 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.loyso.art/frx/kurious/internal/common/config"
|
|
||||||
"git.loyso.art/frx/kurious/internal/common/nullable"
|
"git.loyso.art/frx/kurious/internal/common/nullable"
|
||||||
"git.loyso.art/frx/kurious/internal/common/xcontext"
|
"git.loyso.art/frx/kurious/internal/common/xcontext"
|
||||||
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||||
"git.loyso.art/frx/kurious/migrations/sqlite"
|
|
||||||
"git.loyso.art/frx/kurious/pkg/xdefault"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"go.opentelemetry.io/otel"
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
_ "modernc.org/sqlite"
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
var sqliteTracer = otel.Tracer("sqlite")
|
|
||||||
|
|
||||||
type sqliteConnection struct {
|
|
||||||
db *sqlx.DB
|
|
||||||
shutdownTimeout time.Duration
|
|
||||||
log *slog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSqliteConnection(ctx context.Context, cfg config.Sqlite, log *slog.Logger) (*sqliteConnection, error) {
|
|
||||||
conn, err := sqlx.Open("sqlite", cfg.DSN)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("openning db connection: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = sqlite.RunMigrations(ctx, conn.DB, log)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("running migrations: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &sqliteConnection{
|
|
||||||
db: conn,
|
|
||||||
log: log,
|
|
||||||
shutdownTimeout: xdefault.WithFallback(cfg.ShutdownTimeout, defaultShutdownTimeout),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *sqliteConnection) Close() error {
|
|
||||||
_, cancel := context.WithTimeout(context.Background(), c.shutdownTimeout)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
return c.db.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *sqliteConnection) CourseRepository() *sqliteCourseRepository {
|
func (c *sqliteConnection) CourseRepository() *sqliteCourseRepository {
|
||||||
return &sqliteCourseRepository{
|
return &sqliteCourseRepository{
|
||||||
db: c.db,
|
db: c.db,
|
||||||
@ -73,7 +36,15 @@ func (r *sqliteCourseRepository) List(
|
|||||||
) (result domain.ListCoursesResult, err error) {
|
) (result domain.ListCoursesResult, err error) {
|
||||||
const queryTemplate = `SELECT %s from courses WHERE 1=1`
|
const queryTemplate = `SELECT %s from courses WHERE 1=1`
|
||||||
|
|
||||||
ctx, span := sqliteTracer.Start(ctx, "sqlite.list")
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "list courses.courses",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
span.RecordError(err)
|
span.RecordError(err)
|
||||||
@ -81,10 +52,6 @@ func (r *sqliteCourseRepository) List(
|
|||||||
span.End()
|
span.End()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if params.NextPageToken != "" && params.Offset > 0 {
|
|
||||||
panic("could not use next_page_token and offset at the same time")
|
|
||||||
}
|
|
||||||
|
|
||||||
query := fmt.Sprintf(queryTemplate, coursesFieldsStr)
|
query := fmt.Sprintf(queryTemplate, coursesFieldsStr)
|
||||||
args := make([]any, 0, 6)
|
args := make([]any, 0, 6)
|
||||||
if params.LearningType != "" {
|
if params.LearningType != "" {
|
||||||
@ -99,12 +66,16 @@ func (r *sqliteCourseRepository) List(
|
|||||||
args = append(args, params.OrganizationID)
|
args = append(args, params.OrganizationID)
|
||||||
query += " AND organization_id = ?"
|
query += " AND organization_id = ?"
|
||||||
}
|
}
|
||||||
if params.NextPageToken != "" {
|
|
||||||
args = append(args, params.NextPageToken)
|
if params.OrderBy == "" {
|
||||||
query += " AND id > ?"
|
params.OrderBy = "id"
|
||||||
}
|
}
|
||||||
|
|
||||||
query += " ORDER BY course_thematic, learning_type, id ASC"
|
direction := "ASC"
|
||||||
|
if !params.Ascending {
|
||||||
|
direction = "DESC"
|
||||||
|
}
|
||||||
|
query += " ORDER BY " + params.OrderBy + " " + direction
|
||||||
|
|
||||||
if params.Limit > 0 {
|
if params.Limit > 0 {
|
||||||
query += " LIMIT ?"
|
query += " LIMIT ?"
|
||||||
@ -115,9 +86,8 @@ func (r *sqliteCourseRepository) List(
|
|||||||
args = append(args, params.Offset)
|
args = append(args, params.Offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
span.SetAttributes(
|
span.SetAttributes(dbStatementAttr.String(query))
|
||||||
attribute.String("query", query),
|
|
||||||
)
|
|
||||||
scanF := func(s rowsScanner) (err error) {
|
scanF := func(s rowsScanner) (err error) {
|
||||||
var cdb sqliteCourseDB
|
var cdb sqliteCourseDB
|
||||||
err = s.StructScan(&cdb)
|
err = s.StructScan(&cdb)
|
||||||
@ -144,8 +114,8 @@ func (r *sqliteCourseRepository) List(
|
|||||||
}
|
}
|
||||||
|
|
||||||
span.SetAttributes(
|
span.SetAttributes(
|
||||||
attribute.Int("items_count", len(result.Courses)),
|
attribute.Int("db.items_count", len(result.Courses)),
|
||||||
attribute.Int("total_items", result.Count),
|
attribute.Int("db.total_items", result.Count),
|
||||||
)
|
)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
@ -155,6 +125,23 @@ func (r *sqliteCourseRepository) ListLearningTypes(
|
|||||||
) (result domain.ListLearningTypeResult, err error) {
|
) (result domain.ListLearningTypeResult, err error) {
|
||||||
const query = "SELECT DISTINCT learning_type FROM courses"
|
const query = "SELECT DISTINCT learning_type FROM courses"
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "list_learning_types courses.courses",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
err = r.db.SelectContext(ctx, &result.LearningTypeIDs, query)
|
err = r.db.SelectContext(ctx, &result.LearningTypeIDs, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, fmt.Errorf("executing query: %w", err)
|
return result, fmt.Errorf("executing query: %w", err)
|
||||||
@ -176,6 +163,23 @@ func (r *sqliteCourseRepository) ListCourseThematics(
|
|||||||
query += " AND learning_type = ?"
|
query += " AND learning_type = ?"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "list courses.course_thematic",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
err = r.db.SelectContext(ctx, &result.CourseThematicIDs, query, args...)
|
err = r.db.SelectContext(ctx, &result.CourseThematicIDs, query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return result, fmt.Errorf("executing query: %w", err)
|
return result, fmt.Errorf("executing query: %w", err)
|
||||||
@ -191,6 +195,24 @@ func (r *sqliteCourseRepository) Get(
|
|||||||
const queryTemplate = `SELECT %s FROM courses WHERE id = ?`
|
const queryTemplate = `SELECT %s FROM courses WHERE id = ?`
|
||||||
|
|
||||||
query := fmt.Sprintf(queryTemplate, coursesFieldsStr)
|
query := fmt.Sprintf(queryTemplate, coursesFieldsStr)
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "get courses.courses",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
var courseDB sqliteCourseDB
|
var courseDB sqliteCourseDB
|
||||||
err = r.db.GetContext(ctx, &courseDB, query, id)
|
err = r.db.GetContext(ctx, &courseDB, query, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -259,14 +281,6 @@ func (r *sqliteCourseRepository) Delete(ctx context.Context, id string) error {
|
|||||||
func (r *sqliteCourseRepository) listCount(ctx context.Context, params domain.ListCoursesParams) (count int, err error) {
|
func (r *sqliteCourseRepository) listCount(ctx context.Context, params domain.ListCoursesParams) (count int, err error) {
|
||||||
const queryTemplate = `SELECT COUNT(id) FROM courses WHERE 1=1`
|
const queryTemplate = `SELECT COUNT(id) FROM courses WHERE 1=1`
|
||||||
|
|
||||||
ctx, span := sqliteTracer.Start(ctx, "sqlite.listCount")
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
span.RecordError(err)
|
|
||||||
}
|
|
||||||
span.End()
|
|
||||||
}()
|
|
||||||
|
|
||||||
query := queryTemplate
|
query := queryTemplate
|
||||||
args := make([]any, 0, 6)
|
args := make([]any, 0, 6)
|
||||||
|
|
||||||
@ -283,6 +297,23 @@ func (r *sqliteCourseRepository) listCount(ctx context.Context, params domain.Li
|
|||||||
query += " AND organization_id = ?"
|
query += " AND organization_id = ?"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "list_count courses.courses",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
err = r.db.GetContext(ctx, &count, query, args...)
|
err = r.db.GetContext(ctx, &count, query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return count, fmt.Errorf("sending query: %w", err)
|
return count, fmt.Errorf("sending query: %w", err)
|
||||||
@ -442,3 +473,12 @@ func (c *sqliteCourseDB) FromDomain(d domain.Course) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *sqliteCourseRepository) mergeAttributes(custom ...attribute.KeyValue) []attribute.KeyValue {
|
||||||
|
outbase := append(
|
||||||
|
getSqliteBaseAttributes(),
|
||||||
|
dbTableAttr.String("courses"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return append(outbase, custom...)
|
||||||
|
}
|
||||||
|
|||||||
@ -104,13 +104,4 @@ func (s *sqliteCourseRepositorySuite) TestListLimitOffset() {
|
|||||||
result, err := cr.List(s.ctx, params)
|
result, err := cr.List(s.ctx, params)
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
s.Empty(result.Courses)
|
s.Empty(result.Courses)
|
||||||
|
|
||||||
params.Offset = 0
|
|
||||||
for i := 0; i < listparts; i++ {
|
|
||||||
result, err := cr.List(s.ctx, params)
|
|
||||||
s.NoError(err)
|
|
||||||
s.Len(result.Courses, listparts)
|
|
||||||
|
|
||||||
params.NextPageToken = result.NextPageToken
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import (
|
|||||||
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -50,7 +52,7 @@ type sqliteLearingCategoryRepository struct {
|
|||||||
log *slog.Logger
|
log *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *sqliteLearingCategoryRepository) Upsert(ctx context.Context, c domain.LearningCategory) error {
|
func (r *sqliteLearingCategoryRepository) Upsert(ctx context.Context, c domain.LearningCategory) (err error) {
|
||||||
const queryTemplate = "INSERT INTO learning_categories (%s)" +
|
const queryTemplate = "INSERT INTO learning_categories (%s)" +
|
||||||
" VALUES (%s)" +
|
" VALUES (%s)" +
|
||||||
" ON CONFLICT(id) DO UPDATE" +
|
" ON CONFLICT(id) DO UPDATE" +
|
||||||
@ -64,7 +66,24 @@ func (r *sqliteLearingCategoryRepository) Upsert(ctx context.Context, c domain.L
|
|||||||
strings.TrimSuffix(strings.Repeat("?,", len(learningCategoryColumns)), ","),
|
strings.TrimSuffix(strings.Repeat("?,", len(learningCategoryColumns)), ","),
|
||||||
)
|
)
|
||||||
|
|
||||||
_, err := r.db.ExecContext(
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "upsert courses.learning_categories",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("INSERT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, err = r.db.ExecContext(
|
||||||
ctx, query,
|
ctx, query,
|
||||||
c.ID,
|
c.ID,
|
||||||
nullableValueAsString(c.Logo),
|
nullableValueAsString(c.Logo),
|
||||||
@ -82,6 +101,23 @@ func (r *sqliteLearingCategoryRepository) List(ctx context.Context) (out []domai
|
|||||||
|
|
||||||
query := fmt.Sprintf(queryTemplate, learningCategoryColumnsStr)
|
query := fmt.Sprintf(queryTemplate, learningCategoryColumnsStr)
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "list courses.learning_categories",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
var categories []learningCategoryDB
|
var categories []learningCategoryDB
|
||||||
err = r.db.SelectContext(ctx, &categories, query)
|
err = r.db.SelectContext(ctx, &categories, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -93,13 +129,31 @@ func (r *sqliteLearingCategoryRepository) List(ctx context.Context) (out []domai
|
|||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *sqliteLearingCategoryRepository) Get(ctx context.Context, id string) (domain.LearningCategory, error) {
|
func (r *sqliteLearingCategoryRepository) Get(ctx context.Context, id string) (category domain.LearningCategory, err error) {
|
||||||
const queryTemplate = "SELECT %s FROM learning_categories WHERE id = ?;"
|
const queryTemplate = "SELECT %s FROM learning_categories WHERE id = ?;"
|
||||||
|
|
||||||
query := fmt.Sprintf(queryTemplate, learningCategoryColumnsStr)
|
query := fmt.Sprintf(queryTemplate, learningCategoryColumnsStr)
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "get courses.learning_categories",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
var cdb learningCategoryDB
|
var cdb learningCategoryDB
|
||||||
err := r.db.GetContext(ctx, &cdb, query, id)
|
err = r.db.GetContext(ctx, &cdb, query, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return domain.LearningCategory{}, domain.ErrNotFound
|
return domain.LearningCategory{}, domain.ErrNotFound
|
||||||
@ -109,3 +163,12 @@ func (r *sqliteLearingCategoryRepository) Get(ctx context.Context, id string) (d
|
|||||||
|
|
||||||
return cdb.AsDomain(), nil
|
return cdb.AsDomain(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *sqliteLearingCategoryRepository) mergeAttributes(custom ...attribute.KeyValue) []attribute.KeyValue {
|
||||||
|
outbase := append(
|
||||||
|
getSqliteBaseAttributes(),
|
||||||
|
dbTableAttr.String("learning_categories"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return append(outbase, custom...)
|
||||||
|
}
|
||||||
|
|||||||
@ -6,10 +6,13 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.loyso.art/frx/kurious/internal/common/xslices"
|
"git.loyso.art/frx/kurious/internal/common/xslices"
|
||||||
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
@ -31,6 +34,22 @@ var (
|
|||||||
organizationColumnsArgsStr = namedArgColumns(organizationColumns)
|
organizationColumnsArgsStr = namedArgColumns(organizationColumns)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type organizationStatDB struct {
|
||||||
|
ID string `db:"id"`
|
||||||
|
ExternalID sql.NullString `db:"external_id"`
|
||||||
|
Name string `db:"name"`
|
||||||
|
CoursesCount uint64 `db:"courses_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s organizationStatDB) AsDomain() domain.OrganizationStat {
|
||||||
|
return domain.OrganizationStat{
|
||||||
|
ID: s.ID,
|
||||||
|
ExternalID: nullStringAsDomain(s.ExternalID),
|
||||||
|
Name: s.Name,
|
||||||
|
CoursesCount: s.CoursesCount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type organizationDB struct {
|
type organizationDB struct {
|
||||||
ID string `db:"id"`
|
ID string `db:"id"`
|
||||||
ExternalID sql.NullString `db:"external_id"`
|
ExternalID sql.NullString `db:"external_id"`
|
||||||
@ -83,10 +102,73 @@ type sqliteOrganizationRepository struct {
|
|||||||
log *slog.Logger
|
log *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *sqliteOrganizationRepository) List(ctx context.Context) (out []domain.Organization, err error) {
|
func (r *sqliteOrganizationRepository) ListStats(
|
||||||
const queryTemplate = `SELECT %s FROM organizations`
|
ctx context.Context,
|
||||||
|
params domain.ListOrganizationsParams,
|
||||||
|
) (out []domain.OrganizationStat, err error) {
|
||||||
|
const query = `SELECT o.id as id, o.external_id as external_id, o.name as name, COUNT(c.id) as courses_count` +
|
||||||
|
` FROM organizations o` +
|
||||||
|
` INNER JOIN courses c ON o.id = c.organization_id` +
|
||||||
|
` GROUP BY o.id, o.external_id, o.name` +
|
||||||
|
` ORDER BY COUNT(c.id) DESC`
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "list_stats courses",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
|
var stats []organizationStatDB
|
||||||
|
err = r.db.SelectContext(ctx, &stats, query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("executing query: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return xslices.Map(stats, asDomainFunc), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *sqliteOrganizationRepository) List(ctx context.Context, params domain.ListOrganizationsParams) (out []domain.Organization, err error) {
|
||||||
|
const queryTemplate = `SELECT %s FROM organizations WHERE 1=1`
|
||||||
query := fmt.Sprintf(queryTemplate, organizationColumnsStr)
|
query := fmt.Sprintf(queryTemplate, organizationColumnsStr)
|
||||||
|
|
||||||
|
args := make([]any, 0, len(params.IDs))
|
||||||
|
if len(params.IDs) > 0 {
|
||||||
|
args = append(
|
||||||
|
args,
|
||||||
|
xslices.Map(params.IDs, func(t string) any { return t })...,
|
||||||
|
)
|
||||||
|
queryParam := strings.TrimSuffix(strings.Repeat("?,", len(args)), ",")
|
||||||
|
query += " AND id IN (" + queryParam + ")"
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "list courses.organizations",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
organizations := make([]organizationDB, 0, 1<<8)
|
organizations := make([]organizationDB, 0, 1<<8)
|
||||||
err = r.db.SelectContext(ctx, &organizations, query)
|
err = r.db.SelectContext(ctx, &organizations, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -109,6 +191,23 @@ func (r *sqliteOrganizationRepository) Get(ctx context.Context, params domain.Ge
|
|||||||
query += " AND external_id = ?"
|
query += " AND external_id = ?"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "get courses.organizations",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("SELECT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
var orgdb organizationDB
|
var orgdb organizationDB
|
||||||
err = r.db.GetContext(ctx, &orgdb, query, args...)
|
err = r.db.GetContext(ctx, &orgdb, query, args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -125,6 +224,23 @@ func (r *sqliteOrganizationRepository) Create(ctx context.Context, params domain
|
|||||||
const queryTemplate = `INSERT INTO organizations (%[1]s) VALUES (%[2]s) RETURNING %[1]s`
|
const queryTemplate = `INSERT INTO organizations (%[1]s) VALUES (%[2]s) RETURNING %[1]s`
|
||||||
query := fmt.Sprintf(queryTemplate, organizationColumnsStr, organizationColumnsArgsStr)
|
query := fmt.Sprintf(queryTemplate, organizationColumnsStr, organizationColumnsArgsStr)
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "create courses.organizations",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("INSERT"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
stmt, err := r.db.PrepareNamedContext(ctx, query)
|
stmt, err := r.db.PrepareNamedContext(ctx, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return out, fmt.Errorf("preparing statement: %w", err)
|
return out, fmt.Errorf("preparing statement: %w", err)
|
||||||
@ -151,8 +267,26 @@ func (r *sqliteOrganizationRepository) Create(ctx context.Context, params domain
|
|||||||
return orgdb.AsDomain(), nil
|
return orgdb.AsDomain(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *sqliteOrganizationRepository) Delete(ctx context.Context, id string) error {
|
func (r *sqliteOrganizationRepository) Delete(ctx context.Context, id string) (err error) {
|
||||||
const query = `DELETE FROM organizations WHERE id = ?`
|
const query = `DELETE FROM organizations WHERE id = ?`
|
||||||
|
|
||||||
|
ctx, span := dbTracer.Start(
|
||||||
|
ctx, "delete courses.organizations",
|
||||||
|
trace.WithSpanKind(trace.SpanKindClient),
|
||||||
|
trace.WithAttributes(
|
||||||
|
r.mergeAttributes(
|
||||||
|
dbOperationAttr.String("DELETE"),
|
||||||
|
dbStatementAttr.String(query),
|
||||||
|
)...,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
span.RecordError(err)
|
||||||
|
}
|
||||||
|
span.End()
|
||||||
|
}()
|
||||||
|
|
||||||
result, err := r.db.ExecContext(ctx, query, id)
|
result, err := r.db.ExecContext(ctx, query, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
@ -168,3 +302,12 @@ func (r *sqliteOrganizationRepository) Delete(ctx context.Context, id string) er
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *sqliteOrganizationRepository) mergeAttributes(custom ...attribute.KeyValue) []attribute.KeyValue {
|
||||||
|
outbase := append(
|
||||||
|
getSqliteBaseAttributes(),
|
||||||
|
dbTableAttr.String("organizaitons"),
|
||||||
|
)
|
||||||
|
|
||||||
|
return append(outbase, custom...)
|
||||||
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ func (s *sqliteOrganzationRepositorySuite) TestList() {
|
|||||||
orgsdb = append(orgsdb, gotOrg)
|
orgsdb = append(orgsdb, gotOrg)
|
||||||
}
|
}
|
||||||
|
|
||||||
gotOrgs, err := s.connection.Organization().List(s.ctx)
|
gotOrgs, err := s.connection.Organization().List(s.ctx, domain.ListOrganizationsParams{})
|
||||||
s.NoError(err)
|
s.NoError(err)
|
||||||
|
|
||||||
compareF := func(lhs, rhs domain.Organization) int {
|
compareF := func(lhs, rhs domain.Organization) int {
|
||||||
|
|||||||
@ -178,7 +178,6 @@ func (r *ydbCourseRepository) List(
|
|||||||
|
|
||||||
opts = append(
|
opts = append(
|
||||||
opts,
|
opts,
|
||||||
table.ValueParam("$id", types.TextValue(params.NextPageToken)),
|
|
||||||
table.ValueParam("$limit", types.Int32Value(int32(params.Limit))),
|
table.ValueParam("$limit", types.Int32Value(int32(params.Limit))),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ type Queries struct {
|
|||||||
ListCourseStatistics query.ListCoursesStatsHandler
|
ListCourseStatistics query.ListCoursesStatsHandler
|
||||||
|
|
||||||
ListOrganzations query.ListOrganizationsHandler
|
ListOrganzations query.ListOrganizationsHandler
|
||||||
|
ListOrganizationsStats query.ListOrganizationsStatsHandler
|
||||||
GetOrganization query.GetOrganizationHandler
|
GetOrganization query.GetOrganizationHandler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,9 @@ type ListCourse struct {
|
|||||||
OrganizationID string
|
OrganizationID string
|
||||||
Keyword string
|
Keyword string
|
||||||
|
|
||||||
|
OrderBy string
|
||||||
|
Ascending bool
|
||||||
|
|
||||||
Limit int
|
Limit int
|
||||||
Offset int
|
Offset int
|
||||||
NextPageToken string
|
NextPageToken string
|
||||||
@ -57,6 +60,8 @@ func (h listCourseHandler) Handle(ctx context.Context, query ListCourse) (out do
|
|||||||
OrganizationID: query.OrganizationID,
|
OrganizationID: query.OrganizationID,
|
||||||
Limit: query.Limit,
|
Limit: query.Limit,
|
||||||
Offset: query.Offset,
|
Offset: query.Offset,
|
||||||
|
OrderBy: query.OrderBy,
|
||||||
|
Ascending: query.Ascending,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return out, fmt.Errorf("listing courses: %w", err)
|
return out, fmt.Errorf("listing courses: %w", err)
|
||||||
|
|||||||
@ -9,7 +9,9 @@ import (
|
|||||||
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ListOrganizations struct{}
|
type ListOrganizations struct {
|
||||||
|
IDs []string
|
||||||
|
}
|
||||||
|
|
||||||
type ListOrganizationsHandler decorator.QueryHandler[ListOrganizations, []domain.Organization]
|
type ListOrganizationsHandler decorator.QueryHandler[ListOrganizations, []domain.Organization]
|
||||||
|
|
||||||
@ -29,7 +31,9 @@ func NewListOrganizationsHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h listOrganizationsHandler) Handle(ctx context.Context, query ListOrganizations) ([]domain.Organization, error) {
|
func (h listOrganizationsHandler) Handle(ctx context.Context, query ListOrganizations) ([]domain.Organization, error) {
|
||||||
organizations, err := h.repo.List(ctx)
|
organizations, err := h.repo.List(ctx, domain.ListOrganizationsParams{
|
||||||
|
IDs: query.IDs,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("listing organizations: %w", err)
|
return nil, fmt.Errorf("listing organizations: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
48
internal/kurious/app/query/listorganizationstats.go
Normal file
48
internal/kurious/app/query/listorganizationstats.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"git.loyso.art/frx/kurious/internal/common/decorator"
|
||||||
|
"git.loyso.art/frx/kurious/internal/kurious/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ListOrganizationsStats struct {
|
||||||
|
IDs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListOrganizationsStatsHandler decorator.QueryHandler[
|
||||||
|
ListOrganizationsStats,
|
||||||
|
[]domain.OrganizationStat,
|
||||||
|
]
|
||||||
|
|
||||||
|
type listOrganizationsStatsHandler struct {
|
||||||
|
repo domain.OrganizationRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewListOrganizationsStatsHandler(
|
||||||
|
repo domain.OrganizationRepository,
|
||||||
|
log *slog.Logger,
|
||||||
|
) ListOrganizationsStatsHandler {
|
||||||
|
h := listOrganizationsStatsHandler{
|
||||||
|
repo: repo,
|
||||||
|
}
|
||||||
|
|
||||||
|
return decorator.AddQueryDecorators(h, log)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h listOrganizationsStatsHandler) Handle(
|
||||||
|
ctx context.Context,
|
||||||
|
query ListOrganizationsStats,
|
||||||
|
) ([]domain.OrganizationStat, error) {
|
||||||
|
stats, err := h.repo.ListStats(ctx, domain.ListOrganizationsParams{
|
||||||
|
IDs: query.IDs,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("listing stats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
@ -20,3 +20,11 @@ type Organization struct {
|
|||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
DeletedAt nullable.Value[time.Time]
|
DeletedAt nullable.Value[time.Time]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrganizationStat struct {
|
||||||
|
ID string
|
||||||
|
ExternalID nullable.Value[string]
|
||||||
|
Name string
|
||||||
|
|
||||||
|
CoursesCount uint64
|
||||||
|
}
|
||||||
|
|||||||
@ -12,9 +12,10 @@ type ListCoursesParams struct {
|
|||||||
CourseThematic string
|
CourseThematic string
|
||||||
OrganizationID string
|
OrganizationID string
|
||||||
|
|
||||||
NextPageToken string
|
|
||||||
Limit int
|
Limit int
|
||||||
Offset int
|
Offset int
|
||||||
|
OrderBy string
|
||||||
|
Ascending bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CreateCourseParams struct {
|
type CreateCourseParams struct {
|
||||||
@ -101,9 +102,14 @@ type CreateOrganizationParams struct {
|
|||||||
LogoLink string
|
LogoLink string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ListOrganizationsParams struct {
|
||||||
|
IDs []string
|
||||||
|
}
|
||||||
|
|
||||||
//go:generate mockery --name OrganizationRepository
|
//go:generate mockery --name OrganizationRepository
|
||||||
type OrganizationRepository interface {
|
type OrganizationRepository interface {
|
||||||
List(context.Context) ([]Organization, error)
|
ListStats(context.Context, ListOrganizationsParams) ([]OrganizationStat, error)
|
||||||
|
List(context.Context, ListOrganizationsParams) ([]Organization, error)
|
||||||
Get(context.Context, GetOrganizationParams) (Organization, error)
|
Get(context.Context, GetOrganizationParams) (Organization, error)
|
||||||
Create(context.Context, CreateOrganizationParams) (Organization, error)
|
Create(context.Context, CreateOrganizationParams) (Organization, error)
|
||||||
Delete(ctx context.Context, id string) error
|
Delete(ctx context.Context, id string) error
|
||||||
@ -111,7 +117,14 @@ type OrganizationRepository interface {
|
|||||||
|
|
||||||
type NotImplementedOrganizationRepository struct{}
|
type NotImplementedOrganizationRepository struct{}
|
||||||
|
|
||||||
func (NotImplementedOrganizationRepository) List(context.Context) ([]Organization, error) {
|
func (NotImplementedOrganizationRepository) ListStats(
|
||||||
|
context.Context,
|
||||||
|
ListOrganizationsParams,
|
||||||
|
) ([]OrganizationStat, error) {
|
||||||
|
return nil, ErrNotImplemented
|
||||||
|
}
|
||||||
|
|
||||||
|
func (NotImplementedOrganizationRepository) List(context.Context, ListOrganizationsParams) ([]Organization, error) {
|
||||||
return nil, ErrNotImplemented
|
return nil, ErrNotImplemented
|
||||||
}
|
}
|
||||||
func (NotImplementedOrganizationRepository) Get(context.Context, GetOrganizationParams) (Organization, error) {
|
func (NotImplementedOrganizationRepository) Get(context.Context, GetOrganizationParams) (Organization, error) {
|
||||||
|
|||||||
@ -72,13 +72,113 @@ templ headerNavbar(page PageKind) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
templ footer() {
|
templ footer() {
|
||||||
<footer>
|
<footer class="text-center text-lg-start bg-body-tertiary text-muted">
|
||||||
<div class="row">
|
<section class="p-2">
|
||||||
<span>(c) kurious, 2024. All rights reserved.</span>
|
<div class="container text-center text-md-start mt-5">
|
||||||
|
<div class="row mt-3">
|
||||||
|
<div class="col-md-3 col-lg-4 col-xl-3 mx-auto mb-4">
|
||||||
|
<h6 class="text-uppercase fw-bold mb-4">
|
||||||
|
<i class="fas fa-gem me-3"></i>Courses
|
||||||
|
</h6>
|
||||||
|
<p>
|
||||||
|
Welcome to Courses, your gateway to learning! Explore a diverse
|
||||||
|
range of courses and advance your skills with us. Join our
|
||||||
|
community and transform your life through education.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 col-lg-2 col-xl-2 mx-auto mb-4">
|
||||||
|
<h6 class="text-uppercase fw-bold mb-4">Useful links</h6>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Pricing</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Settings</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Orders</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="#!" class="text-reset">Help</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4 col-lg-3 col-xl-3 mx-auto mb-md-0 mb-4">
|
||||||
|
<h6 class="text-uppercase fw-bold mb-4">Contact</h6>
|
||||||
|
<p><i class="fas fa-home me-3"></i> New York, NY 10012, US</p>
|
||||||
|
<p>
|
||||||
|
<i class="fas fa-envelope me-3"></i>
|
||||||
|
info@example.com
|
||||||
|
</p>
|
||||||
|
<p><i class="fas fa-phone me-3"></i> + 01 234 567 88</p>
|
||||||
|
<p><i class="fas fa-print me-3"></i> + 01 234 567 89</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<div class="text-center p-4" style="background-color: rgba(0, 0, 0, 0.05)">
|
||||||
|
© 2024 Copyright:
|
||||||
|
<a class="text-reset fw-bold" href="https://mdbootstrap.com/">kursov.net</a>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
script elementScriptsLoad() {
|
||||||
|
const formFilterOnSubmit = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const lt = document.getElementById('learning-type-filter');
|
||||||
|
const ct = document.getElementById('course-thematic-filter');
|
||||||
|
|
||||||
|
const prefix = (lt !== null && lt.value !== '') ? `/courses/${lt.value}` : `/courses`;
|
||||||
|
const out = (ct !== null && ct.value !== '') ? `${prefix}/${ct.value}` : prefix;
|
||||||
|
|
||||||
|
document.location.assign(out);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterBySchool = () => {
|
||||||
|
const school_selector = document.getElementById('schoolSelect');
|
||||||
|
const school_id = (school_selector !== null && school_selector.value !== '') ? school_selector.value : '';
|
||||||
|
|
||||||
|
const order_by = document.getElementById('sortBySelect');
|
||||||
|
const order_by_value = (order_by !== null && order_by.value !== '') ? order_by.value : '';
|
||||||
|
|
||||||
|
const ascending = document.getElementById('sortByOrder');
|
||||||
|
|
||||||
|
const baseUrl = `${window.location.pathname}?`;
|
||||||
|
|
||||||
|
params = [];
|
||||||
|
if (school_id) {
|
||||||
|
params.push(`school_id=${school_id}`);
|
||||||
|
};
|
||||||
|
if (order_by_value) {
|
||||||
|
params.push(`order_by=${order_by_value}`);
|
||||||
|
};
|
||||||
|
if (ascending && ascending.checked) {
|
||||||
|
params.push(`asc=true`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const finalUrl = baseUrl + params.join("&");
|
||||||
|
if (history.pushState) {
|
||||||
|
// history.pushState(null, null, finalUrl);
|
||||||
|
// TODO: remove once htmx implemented
|
||||||
|
window.location.assign(finalUrl);
|
||||||
|
} else {
|
||||||
|
window.location.assign(finalUrl);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const ff = document.getElementById('filter-form');
|
||||||
|
if (ff) ff.addEventListener('submit', formFilterOnSubmit);
|
||||||
|
|
||||||
|
const fs = document.getElementById('schoolSelect');
|
||||||
|
if (fs) fs.onchange = filterBySchool;
|
||||||
|
|
||||||
|
const ob = document.getElementById('sortBySelect');
|
||||||
|
if (ob) ob.onchange = filterBySchool;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
templ root(page PageKind, _ stats) {
|
templ root(page PageKind, _ stats) {
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="ru">
|
<html lang="ru">
|
||||||
@ -88,7 +188,8 @@ templ root(page PageKind, _ stats) {
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
{ children... }
|
{ children... }
|
||||||
</div>
|
</div>
|
||||||
@breadcrumbsLoad()
|
@elementScriptsLoad()
|
||||||
</body>
|
</body>
|
||||||
|
@footer()
|
||||||
</html>
|
</html>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,16 +181,151 @@ func footer() templ.Component {
|
|||||||
templ_7745c5c3_Var12 = templ.NopComponent
|
templ_7745c5c3_Var12 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<footer><div class=\"row\"><span>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<footer class=\"text-center text-lg-start bg-body-tertiary text-muted\"><section class=\"p-2\"><div class=\"container text-center text-md-start mt-5\"><div class=\"row mt-3\"><div class=\"col-md-3 col-lg-4 col-xl-3 mx-auto mb-4\"><h6 class=\"text-uppercase fw-bold mb-4\"><i class=\"fas fa-gem me-3\"></i>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var13 := `(c) kurious, 2024. All rights reserved.`
|
templ_7745c5c3_Var13 := `Courses`
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</span></div></footer>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h6><p>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var14 := `Welcome to Courses, your gateway to learning! Explore a diverse`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var14)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var15 := `range of courses and advance your skills with us. Join our`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var16 := `community and transform your life through education.`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p></div><div class=\"col-md-3 col-lg-2 col-xl-2 mx-auto mb-4\"><h6 class=\"text-uppercase fw-bold mb-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var17 := `Useful links`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h6><p><a href=\"#!\" class=\"text-reset\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var18 := `Pricing`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></p><p><a href=\"#!\" class=\"text-reset\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var19 := `Settings`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></p><p><a href=\"#!\" class=\"text-reset\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var20 := `Orders`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var20)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></p><p><a href=\"#!\" class=\"text-reset\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var21 := `Help`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var21)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></p></div><div class=\"col-md-4 col-lg-3 col-xl-3 mx-auto mb-md-0 mb-4\"><h6 class=\"text-uppercase fw-bold mb-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var22 := `Contact`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var22)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</h6><p><i class=\"fas fa-home me-3\"></i> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var23 := `New York, NY 10012, US`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p><p><i class=\"fas fa-envelope me-3\"></i> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var24 := `info@example.com`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p><p><i class=\"fas fa-phone me-3\"></i> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var25 := `+ 01 234 567 88`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p><p><i class=\"fas fa-print me-3\"></i> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var26 := `+ 01 234 567 89`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var26)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</p></div></div></div></section><div class=\"text-center p-4\" style=\"background-color: rgba(0, 0, 0, 0.05)\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var27 := `© 2024 Copyright:`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <a class=\"text-reset fw-bold\" href=\"https://mdbootstrap.com/\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var28 := `kursov.net`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var28)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</a></div></footer>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -201,6 +336,69 @@ func footer() templ.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func elementScriptsLoad() templ.ComponentScript {
|
||||||
|
return templ.ComponentScript{
|
||||||
|
Name: `__templ_elementScriptsLoad_873d`,
|
||||||
|
Function: `function __templ_elementScriptsLoad_873d(){const formFilterOnSubmit = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const lt = document.getElementById('learning-type-filter');
|
||||||
|
const ct = document.getElementById('course-thematic-filter');
|
||||||
|
|
||||||
|
const prefix = (lt !== null && lt.value !== '') ? ` + "`" + `/courses/${lt.value}` + "`" + ` : ` + "`" + `/courses` + "`" + `;
|
||||||
|
const out = (ct !== null && ct.value !== '') ? ` + "`" + `${prefix}/${ct.value}` + "`" + ` : prefix;
|
||||||
|
|
||||||
|
document.location.assign(out);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterBySchool = () => {
|
||||||
|
const school_selector = document.getElementById('schoolSelect');
|
||||||
|
const school_id = (school_selector !== null && school_selector.value !== '') ? school_selector.value : '';
|
||||||
|
|
||||||
|
const order_by = document.getElementById('sortBySelect');
|
||||||
|
const order_by_value = (order_by !== null && order_by.value !== '') ? order_by.value : '';
|
||||||
|
|
||||||
|
const ascending = document.getElementById('sortByOrder');
|
||||||
|
|
||||||
|
const baseUrl = ` + "`" + `${window.location.pathname}?` + "`" + `;
|
||||||
|
|
||||||
|
params = [];
|
||||||
|
if (school_id) {
|
||||||
|
params.push(` + "`" + `school_id=${school_id}` + "`" + `);
|
||||||
|
};
|
||||||
|
if (order_by_value) {
|
||||||
|
params.push(` + "`" + `order_by=${order_by_value}` + "`" + `);
|
||||||
|
};
|
||||||
|
if (ascending && ascending.checked) {
|
||||||
|
params.push(` + "`" + `asc=true` + "`" + `);
|
||||||
|
};
|
||||||
|
|
||||||
|
const finalUrl = baseUrl + params.join("&");
|
||||||
|
if (history.pushState) {
|
||||||
|
// history.pushState(null, null, finalUrl);
|
||||||
|
// TODO: remove once htmx implemented
|
||||||
|
window.location.assign(finalUrl);
|
||||||
|
} else {
|
||||||
|
window.location.assign(finalUrl);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const ff = document.getElementById('filter-form');
|
||||||
|
if (ff) ff.addEventListener('submit', formFilterOnSubmit);
|
||||||
|
|
||||||
|
const fs = document.getElementById('schoolSelect');
|
||||||
|
if (fs) fs.onchange = filterBySchool;
|
||||||
|
|
||||||
|
const ob = document.getElementById('sortBySelect');
|
||||||
|
if (ob) ob.onchange = filterBySchool;
|
||||||
|
});}`,
|
||||||
|
Call: templ.SafeScript(`__templ_elementScriptsLoad_873d`),
|
||||||
|
CallInline: templ.SafeScriptInline(`__templ_elementScriptsLoad_873d`),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func root(page PageKind, _ stats) templ.Component {
|
func root(page PageKind, _ stats) templ.Component {
|
||||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
@ -209,9 +407,9 @@ func root(page PageKind, _ stats) templ.Component {
|
|||||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var14 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var29 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var14 == nil {
|
if templ_7745c5c3_Var29 == nil {
|
||||||
templ_7745c5c3_Var14 = templ.NopComponent
|
templ_7745c5c3_Var29 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"ru\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<!doctype html><html lang=\"ru\">")
|
||||||
@ -234,7 +432,7 @@ func root(page PageKind, _ stats) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templ_7745c5c3_Var14.Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = templ_7745c5c3_Var29.Render(ctx, templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -242,11 +440,19 @@ func root(page PageKind, _ stats) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = breadcrumbsLoad().Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = elementScriptsLoad().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body></html>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</body>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = footer().Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</html>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,27 +3,6 @@ package bootstrap
|
|||||||
import "path"
|
import "path"
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
|
||||||
script breadcrumbsLoad() {
|
|
||||||
const formFilterOnSubmit = event => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const lt = document.getElementById('learning-type-filter');
|
|
||||||
const ct = document.getElementById('course-thematic-filter');
|
|
||||||
|
|
||||||
const prefix = (lt !== null && lt.value !== '') ? `/courses/${lt.value}` : `/courses`;
|
|
||||||
const out = (ct !== null && ct.value !== '') ? `${prefix}/${ct.value}` : prefix;
|
|
||||||
|
|
||||||
document.location.assign(out);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const ff = document.getElementById('filter-form');
|
|
||||||
if (ff === null) return;
|
|
||||||
ff.addEventListener('submit', formFilterOnSubmit);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
templ breadcrumbsItem(text, link string, isActive bool) {
|
templ breadcrumbsItem(text, link string, isActive bool) {
|
||||||
<li class={ "breadcrumb-item", templ.KV("active", isActive) }>
|
<li class={ "breadcrumb-item", templ.KV("active", isActive) }>
|
||||||
if link != "" {
|
if link != "" {
|
||||||
@ -78,7 +57,12 @@ templ listCoursesSectionHeader(params BreadcrumbsParams) {
|
|||||||
|
|
||||||
templ listCoursesSectionFilters(params FilterFormParams) {
|
templ listCoursesSectionFilters(params FilterFormParams) {
|
||||||
<section class="row filters">
|
<section class="row filters">
|
||||||
<div class="col-8">
|
<div
|
||||||
|
class={
|
||||||
|
templ.KV("visually-hidden", !params.Render),
|
||||||
|
"p-2",
|
||||||
|
}
|
||||||
|
>
|
||||||
<form id="filter-form" class="input-group">
|
<form id="filter-form" class="input-group">
|
||||||
<span class="input-group-text">Filter courses</span>
|
<span class="input-group-text">Filter courses</span>
|
||||||
<select
|
<select
|
||||||
@ -108,21 +92,95 @@ templ listCoursesSectionFilters(params FilterFormParams) {
|
|||||||
<button id="filter-course-thematic" class="btn btn-outline-secondary" type="submit">Go</button>
|
<button id="filter-course-thematic" class="btn btn-outline-secondary" type="submit">Go</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@filterCoursesSelectView(params.Schools)
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
|
|
||||||
templ listCoursesLearning(containers []CategoryContainer) {
|
templ selectOptionFromPairs(emptyName, activeID string, items []NameIDPair) {
|
||||||
|
<option value="" selected?={ activeID == "" }>
|
||||||
|
{ emptyName }
|
||||||
|
</option>
|
||||||
|
for _, item := range items {
|
||||||
|
<option
|
||||||
|
selected?={ activeID==item.ID }
|
||||||
|
value={ item.ID }
|
||||||
|
>
|
||||||
|
{ item.Name }
|
||||||
|
</option>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
templ filterCoursesSelectView(params CoursesFilterViewParams) {
|
||||||
|
<div class="filter-content d-flex p-2">
|
||||||
|
<!-- School list -->
|
||||||
|
<div class="col-3">
|
||||||
|
<select name="school" id="schoolSelect" class="form-select" aria-label="school filter">
|
||||||
|
@selectOptionFromPairs("Pick a school:", params.SelectedSchoolID, params.Schools)
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<!-- Sort options -->
|
||||||
|
<div class="col-3 mx-4">
|
||||||
|
<div class="input-group flex-nowrap">
|
||||||
|
<select
|
||||||
|
class="form-select"
|
||||||
|
id="sortBySelect"
|
||||||
|
aria-label="Sorting items by requested order"
|
||||||
|
>
|
||||||
|
@selectOptionFromPairs("Sort by:", params.OrderBy, params.OrderFields)
|
||||||
|
</select>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
class="btn-check"
|
||||||
|
name="sortByOrder"
|
||||||
|
id="sortByOrder"
|
||||||
|
autocomplete="off"
|
||||||
|
checked?={ !params.Ascending }
|
||||||
|
/>
|
||||||
|
<label class="btn" for="sortByOrder">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
fill="currentColor"
|
||||||
|
class="bi bi-sort-down"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M3.5 2.5a.5.5 0 0 0-1 0v8.793l-1.146-1.147a.5.5 0 0 0-.708.708l2 1.999.007.007a.497.497 0 0 0 .7-.006l2-2a.5.5 0 0 0-.707-.708L3.5 11.293zm3.5 1a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M7.5 6a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-4"></div>
|
||||||
|
<div class="col-auto ms-auto">
|
||||||
|
<div class="btn btn-primary">Promocodes</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ listCoursesLearning(courses []CourseInfo) {
|
||||||
|
<div class="block">
|
||||||
|
<div class="row row-cols-1 row-cols-md-4 g-4">
|
||||||
|
for _, course := range courses {
|
||||||
|
@listCoursesCard(course)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
templ listCoursesLearningLegacy(containers []CategoryContainer) {
|
||||||
for _, container := range containers {
|
for _, container := range containers {
|
||||||
<section class="row first-class-group g-4">
|
<section class="row first-class-group g-4">
|
||||||
<h1 class="title">{ container.Name }</h1>
|
<h1 class="title">{ container.Name }</h1>
|
||||||
for _, subcategory := range container.Subcategories {
|
for _, subcategory := range container.Subcategories {
|
||||||
@listCoursesThematicRow(container.ID, subcategory)
|
@listCoursesThematicRowLegacy(container.ID, subcategory)
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
templ listCoursesThematicRow(categoryID string, subcategory SubcategoryContainer) {
|
templ listCoursesThematicRowLegacy(categoryID string, subcategory SubcategoryContainer) {
|
||||||
<div class="block second-class-group">
|
<div class="block second-class-group">
|
||||||
<h3 class="title">
|
<h3 class="title">
|
||||||
<a href={ templ.SafeURL("/courses/" + categoryID + "/" + subcategory.ID) } class="text-decoration-none">
|
<a href={ templ.SafeURL("/courses/" + categoryID + "/" + subcategory.ID) } class="text-decoration-none">
|
||||||
@ -152,7 +210,7 @@ css cardTextSize() {
|
|||||||
|
|
||||||
templ listCoursesCard(info CourseInfo) {
|
templ listCoursesCard(info CourseInfo) {
|
||||||
// <div class="col-12 col-md-6 col-lg-3">
|
// <div class="col-12 col-md-6 col-lg-3">
|
||||||
<div class="col">
|
<div class="col" id={ info.ID }>
|
||||||
<div class="card h-100">
|
<div class="card h-100">
|
||||||
<img src={ GetOrFallback(info.ImageLink, "https://placehold.co/128x128") } alt="Course picture" class={ "card-img-top" }/>
|
<img src={ GetOrFallback(info.ImageLink, "https://placehold.co/128x128") } alt="Course picture" class={ "card-img-top" }/>
|
||||||
<div class={ "card-body", cardTextSize(), "row" }>
|
<div class={ "card-body", cardTextSize(), "row" }>
|
||||||
@ -175,7 +233,7 @@ templ ListCourses(pageType PageKind, s stats, params ListCoursesParams) {
|
|||||||
@root(pageType, s) {
|
@root(pageType, s) {
|
||||||
@listCoursesSectionHeader(params.FilterForm.BreadcrumbsParams)
|
@listCoursesSectionHeader(params.FilterForm.BreadcrumbsParams)
|
||||||
@listCoursesSectionFilters(params.FilterForm)
|
@listCoursesSectionFilters(params.FilterForm)
|
||||||
@listCoursesLearning(params.Categories)
|
@listCoursesLearning(params.Courses)
|
||||||
@pagination(params.Pagination)
|
@pagination(params.Pagination)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,32 +14,6 @@ import "strings"
|
|||||||
import "path"
|
import "path"
|
||||||
import "strconv"
|
import "strconv"
|
||||||
|
|
||||||
func breadcrumbsLoad() templ.ComponentScript {
|
|
||||||
return templ.ComponentScript{
|
|
||||||
Name: `__templ_breadcrumbsLoad_e656`,
|
|
||||||
Function: `function __templ_breadcrumbsLoad_e656(){const formFilterOnSubmit = event => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const lt = document.getElementById('learning-type-filter');
|
|
||||||
const ct = document.getElementById('course-thematic-filter');
|
|
||||||
|
|
||||||
const prefix = (lt !== null && lt.value !== '') ? ` + "`" + `/courses/${lt.value}` + "`" + ` : ` + "`" + `/courses` + "`" + `;
|
|
||||||
const out = (ct !== null && ct.value !== '') ? ` + "`" + `${prefix}/${ct.value}` + "`" + ` : prefix;
|
|
||||||
|
|
||||||
document.location.assign(out);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
const ff = document.getElementById('filter-form');
|
|
||||||
if (ff === null) return;
|
|
||||||
ff.addEventListener('submit', formFilterOnSubmit);
|
|
||||||
});}`,
|
|
||||||
Call: templ.SafeScript(`__templ_breadcrumbsLoad_e656`),
|
|
||||||
CallInline: templ.SafeScriptInline(`__templ_breadcrumbsLoad_e656`),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func breadcrumbsItem(text, link string, isActive bool) templ.Component {
|
func breadcrumbsItem(text, link string, isActive bool) templ.Component {
|
||||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
@ -87,7 +61,7 @@ func breadcrumbsItem(text, link string, isActive bool) templ.Component {
|
|||||||
var templ_7745c5c3_Var4 string
|
var templ_7745c5c3_Var4 string
|
||||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 33, Col: 10}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 12, Col: 10}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@ -105,7 +79,7 @@ func breadcrumbsItem(text, link string, isActive bool) templ.Component {
|
|||||||
var templ_7745c5c3_Var5 string
|
var templ_7745c5c3_Var5 string
|
||||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(text)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 35, Col: 47}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 14, Col: 47}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
@ -247,12 +221,32 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
templ_7745c5c3_Var9 = templ.NopComponent
|
templ_7745c5c3_Var9 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<section class=\"row filters\"><div class=\"col-8\"><form id=\"filter-form\" class=\"input-group\"><span class=\"input-group-text\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<section class=\"row filters\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var10 := `Filter courses`
|
var templ_7745c5c3_Var10 = []any{
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var10)
|
templ.KV("visually-hidden", !params.Render),
|
||||||
|
"p-2",
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var10...)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var10).String()))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><form id=\"filter-form\" class=\"input-group\"><span class=\"input-group-text\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var11 := `Filter courses`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var11)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -260,8 +254,8 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var11 = []any{"form-select"}
|
var templ_7745c5c3_Var12 = []any{"form-select"}
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var11...)
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -269,7 +263,7 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var11).String()))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var12).String()))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -287,8 +281,8 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var12 := `All`
|
templ_7745c5c3_Var13 := `All`
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var12)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var13)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -319,12 +313,12 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var13 string
|
var templ_7745c5c3_Var14 string
|
||||||
templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(learningType.Name)
|
templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(learningType.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 92, Col: 26}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 76, Col: 26}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -337,8 +331,8 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var14 = []any{"form-select", templ.KV("d-none", len(params.AvailableCourseThematics) == 0)}
|
var templ_7745c5c3_Var15 = []any{"form-select", templ.KV("d-none", len(params.AvailableCourseThematics) == 0)}
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var14...)
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var15...)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -346,7 +340,7 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var14).String()))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var15).String()))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -364,8 +358,8 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var15 := `All`
|
templ_7745c5c3_Var16 := `All`
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var15)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -396,12 +390,12 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var16 string
|
var templ_7745c5c3_Var17 string
|
||||||
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(courseThematic.Name)
|
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(courseThematic.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 104, Col: 28}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 88, Col: 28}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -414,12 +408,20 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var17 := `Go`
|
templ_7745c5c3_Var18 := `Go`
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button></form></div></section>")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</button></form></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = filterCoursesSelectView(params.Schools).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</section>")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -430,7 +432,7 @@ func listCoursesSectionFilters(params FilterFormParams) templ.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func listCoursesLearning(containers []CategoryContainer) templ.Component {
|
func selectOptionFromPairs(emptyName, activeID string, items []NameIDPair) templ.Component {
|
||||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
if !templ_7745c5c3_IsBuffer {
|
if !templ_7745c5c3_IsBuffer {
|
||||||
@ -438,22 +440,217 @@ func listCoursesLearning(containers []CategoryContainer) templ.Component {
|
|||||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var18 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var19 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var18 == nil {
|
if templ_7745c5c3_Var19 == nil {
|
||||||
templ_7745c5c3_Var18 = templ.NopComponent
|
templ_7745c5c3_Var19 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
for _, container := range containers {
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option value=\"\"")
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<section class=\"row first-class-group\"><h1 class=\"title\">")
|
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var19 string
|
if activeID == "" {
|
||||||
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(container.Name)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" selected")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 116, Col: 37}
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19))
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var20 string
|
||||||
|
templ_7745c5c3_Var20, templ_7745c5c3_Err = templ.JoinStringErrs(emptyName)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 100, Col: 13}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var20))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option> ")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, item := range items {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<option")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if activeID == item.ID {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" selected")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" value=\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(item.ID))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var21 string
|
||||||
|
templ_7745c5c3_Var21, templ_7745c5c3_Err = templ.JoinStringErrs(item.Name)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 107, Col: 14}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var21))
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</option>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func filterCoursesSelectView(params CoursesFilterViewParams) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var22 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var22 == nil {
|
||||||
|
templ_7745c5c3_Var22 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"filter-content d-flex p-2\"><!--")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var23 := ` School list `
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("--><div class=\"col-3\"><select name=\"school\" id=\"schoolSelect\" class=\"form-select\" aria-label=\"school filter\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = selectOptionFromPairs("Pick a school:", params.SelectedSchoolID, params.Schools).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select></div><!--")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var24 := ` Sort options `
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var24)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("--><div class=\"col-3 mx-4\"><div class=\"input-group flex-nowrap\"><select class=\"form-select\" id=\"sortBySelect\" aria-label=\"Sorting items by requested order\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Err = selectOptionFromPairs("Sort by:", params.OrderBy, params.OrderFields).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</select> <input type=\"radio\" class=\"btn-check\" name=\"sortByOrder\" id=\"sortByOrder\" autocomplete=\"off\"")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !params.Ascending {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" checked")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("> <label class=\"btn\" for=\"sortByOrder\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-sort-down\" viewBox=\"0 0 16 16\"><path d=\"M3.5 2.5a.5.5 0 0 0-1 0v8.793l-1.146-1.147a.5.5 0 0 0-.708.708l2 1.999.007.007a.497.497 0 0 0 .7-.006l2-2a.5.5 0 0 0-.707-.708L3.5 11.293zm3.5 1a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5M7.5 6a.5.5 0 0 0 0 1h5a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h3a.5.5 0 0 0 0-1zm0 3a.5.5 0 0 0 0 1h1a.5.5 0 0 0 0-1z\"></path></svg></label></div></div><div class=\"col-4\"></div><div class=\"col-auto ms-auto\"><div class=\"btn btn-primary\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
templ_7745c5c3_Var25 := `Promocodes`
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func listCoursesLearning(courses []CourseInfo) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var26 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var26 == nil {
|
||||||
|
templ_7745c5c3_Var26 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"block\"><div class=\"row row-cols-1 row-cols-md-4 g-4\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
for _, course := range courses {
|
||||||
|
templ_7745c5c3_Err = listCoursesCard(course).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("</div></div>")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||||
|
}
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func listCoursesLearningLegacy(containers []CategoryContainer) templ.Component {
|
||||||
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
|
if !templ_7745c5c3_IsBuffer {
|
||||||
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
|
}
|
||||||
|
ctx = templ.InitializeContext(ctx)
|
||||||
|
templ_7745c5c3_Var27 := templ.GetChildren(ctx)
|
||||||
|
if templ_7745c5c3_Var27 == nil {
|
||||||
|
templ_7745c5c3_Var27 = templ.NopComponent
|
||||||
|
}
|
||||||
|
ctx = templ.ClearChildren(ctx)
|
||||||
|
for _, container := range containers {
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<section class=\"row first-class-group g-4\"><h1 class=\"title\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var28 string
|
||||||
|
templ_7745c5c3_Var28, templ_7745c5c3_Err = templ.JoinStringErrs(container.Name)
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 174, Col: 37}
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var28))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -462,7 +659,7 @@ func listCoursesLearning(containers []CategoryContainer) templ.Component {
|
|||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
for _, subcategory := range container.Subcategories {
|
for _, subcategory := range container.Subcategories {
|
||||||
templ_7745c5c3_Err = listCoursesThematicRow(container.ID, subcategory).Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = listCoursesThematicRowLegacy(container.ID, subcategory).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -479,7 +676,7 @@ func listCoursesLearning(containers []CategoryContainer) templ.Component {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func listCoursesThematicRow(categoryID string, subcategory SubcategoryContainer) templ.Component {
|
func listCoursesThematicRowLegacy(categoryID string, subcategory SubcategoryContainer) templ.Component {
|
||||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
if !templ_7745c5c3_IsBuffer {
|
if !templ_7745c5c3_IsBuffer {
|
||||||
@ -487,17 +684,17 @@ func listCoursesThematicRow(categoryID string, subcategory SubcategoryContainer)
|
|||||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var20 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var29 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var20 == nil {
|
if templ_7745c5c3_Var29 == nil {
|
||||||
templ_7745c5c3_Var20 = templ.NopComponent
|
templ_7745c5c3_Var29 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"block second-class-group\"><h2 class=\"title\"><a href=\"")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"block second-class-group\"><h3 class=\"title\"><a href=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var21 templ.SafeURL = templ.SafeURL("/courses/" + categoryID + "/" + subcategory.ID)
|
var templ_7745c5c3_Var30 templ.SafeURL = templ.SafeURL("/courses/" + categoryID + "/" + subcategory.ID)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var21)))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var30)))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -505,30 +702,30 @@ func listCoursesThematicRow(categoryID string, subcategory SubcategoryContainer)
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var22 string
|
var templ_7745c5c3_Var31 string
|
||||||
templ_7745c5c3_Var22, templ_7745c5c3_Err = templ.JoinStringErrs(subcategory.Name)
|
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(subcategory.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 128, Col: 22}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 186, Col: 22}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var22))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-up-right-square\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1zM0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm5.854 8.803a.5.5 0 1 1-.708-.707L9.243 6H6.475a.5.5 0 1 1 0-1h3.975a.5.5 0 0 1 .5.5v3.975a.5.5 0 1 1-1 0V6.707z\"></path></svg></a></h2><p class=\"visually-hidden\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-arrow-up-right-square\" viewBox=\"0 0 16 16\"><path fill-rule=\"evenodd\" d=\"M15 2a1 1 0 0 0-1-1H2a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1zM0 2a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2zm5.854 8.803a.5.5 0 1 1-.708-.707L9.243 6H6.475a.5.5 0 1 1 0-1h3.975a.5.5 0 0 1 .5.5v3.975a.5.5 0 1 1-1 0V6.707z\"></path></svg></a></h3><p class=\"visually-hidden\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var23 := `В категогрии `
|
templ_7745c5c3_Var32 := `В категогрии `
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var23)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var32)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var24 string
|
var templ_7745c5c3_Var33 string
|
||||||
templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(subcategory.Name)
|
templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(subcategory.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 134, Col: 71}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 192, Col: 71}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -536,17 +733,17 @@ func listCoursesThematicRow(categoryID string, subcategory SubcategoryContainer)
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var25 := `собраны `
|
templ_7745c5c3_Var34 := `собраны `
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var25)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var34)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var26 string
|
var templ_7745c5c3_Var35 string
|
||||||
templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(subcategory.Count))
|
templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(subcategory.Count))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 134, Col: 122}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 192, Col: 122}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -554,8 +751,8 @@ func listCoursesThematicRow(categoryID string, subcategory SubcategoryContainer)
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var27 := `курсов. Раз в неделю мы обновляем информацию о всех курсах.`
|
templ_7745c5c3_Var36 := `курсов. Раз в неделю мы обновляем информацию о всех курсах.`
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var27)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var36)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -609,17 +806,25 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var28 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var37 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var28 == nil {
|
if templ_7745c5c3_Var37 == nil {
|
||||||
templ_7745c5c3_Var28 = templ.NopComponent
|
templ_7745c5c3_Var37 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col\"><div class=\"card h-100\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<div class=\"col\" id=\"")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var29 = []any{"card-img-top"}
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(info.ID))
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var29...)
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("\"><div class=\"card h-100\">")
|
||||||
|
if templ_7745c5c3_Err != nil {
|
||||||
|
return templ_7745c5c3_Err
|
||||||
|
}
|
||||||
|
var templ_7745c5c3_Var38 = []any{"card-img-top"}
|
||||||
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var38...)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -635,7 +840,7 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var29).String()))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var38).String()))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -643,8 +848,8 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var30 = []any{"card-body", cardTextSize(), "row"}
|
var templ_7745c5c3_Var39 = []any{"card-body", cardTextSize(), "row"}
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var30...)
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var39...)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -652,7 +857,7 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var30).String()))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ.CSSClasses(templ_7745c5c3_Var39).String()))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -660,12 +865,12 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var31 string
|
var templ_7745c5c3_Var40 string
|
||||||
templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(info.Name)
|
templ_7745c5c3_Var40, templ_7745c5c3_Err = templ.JoinStringErrs(info.Name)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 158, Col: 38}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 216, Col: 38}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var40))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -673,8 +878,8 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var32 templ.SafeURL = templ.URL(info.OriginLink)
|
var templ_7745c5c3_Var41 templ.SafeURL = templ.URL(info.OriginLink)
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var32)))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var41)))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -682,8 +887,8 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var33 := `Go!`
|
templ_7745c5c3_Var42 := `Go!`
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var33)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var42)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -691,12 +896,12 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var34 string
|
var templ_7745c5c3_Var43 string
|
||||||
templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(info.FullPrice))
|
templ_7745c5c3_Var43, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(info.FullPrice))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 165, Col: 36}
|
return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/kurious/ports/http/bootstrap/list.templ`, Line: 223, Col: 36}
|
||||||
}
|
}
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34))
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var43))
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -704,8 +909,8 @@ func listCoursesCard(info CourseInfo) templ.Component {
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Var35 := `rub.`
|
templ_7745c5c3_Var44 := `rub.`
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var35)
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var44)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -728,12 +933,12 @@ func ListCourses(pageType PageKind, s stats, params ListCoursesParams) templ.Com
|
|||||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||||
}
|
}
|
||||||
ctx = templ.InitializeContext(ctx)
|
ctx = templ.InitializeContext(ctx)
|
||||||
templ_7745c5c3_Var36 := templ.GetChildren(ctx)
|
templ_7745c5c3_Var45 := templ.GetChildren(ctx)
|
||||||
if templ_7745c5c3_Var36 == nil {
|
if templ_7745c5c3_Var45 == nil {
|
||||||
templ_7745c5c3_Var36 = templ.NopComponent
|
templ_7745c5c3_Var45 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
templ_7745c5c3_Var37 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
templ_7745c5c3_Var46 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||||
if !templ_7745c5c3_IsBuffer {
|
if !templ_7745c5c3_IsBuffer {
|
||||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||||
@ -755,7 +960,7 @@ func ListCourses(pageType PageKind, s stats, params ListCoursesParams) templ.Com
|
|||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = listCoursesLearning(params.Categories).Render(ctx, templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = listCoursesLearning(params.Courses).Render(ctx, templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
@ -772,7 +977,7 @@ func ListCourses(pageType PageKind, s stats, params ListCoursesParams) templ.Com
|
|||||||
}
|
}
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
})
|
})
|
||||||
templ_7745c5c3_Err = root(pageType, s).Render(templ.WithChildren(ctx, templ_7745c5c3_Var37), templ_7745c5c3_Buffer)
|
templ_7745c5c3_Err = root(pageType, s).Render(templ.WithChildren(ctx, templ_7745c5c3_Var46), templ_7745c5c3_Buffer)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,13 +63,13 @@ type Pagination struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
templ pagination(p Pagination) {
|
templ pagination(p Pagination) {
|
||||||
if p.Page > 0 {
|
if p.Page > 0 && p.TotalPages > 0 {
|
||||||
<nav aria-label="Page navigation">
|
<nav aria-label="Page navigation">
|
||||||
<ul class="pagination justify-content-center">
|
<ul class="pagination justify-content-center">
|
||||||
<li
|
<li
|
||||||
class={
|
class={
|
||||||
"page-item",
|
"page-item",
|
||||||
templ.KV("disabled", p.Page > 0 && p.Page == 1),
|
templ.KV("disabled", p.Page == 1),
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<a href={ templ.URL(p.BaseURL + "?page=" + strconv.Itoa(p.Page-1)) } class="page-link">Previous</a>
|
<a href={ templ.URL(p.BaseURL + "?page=" + strconv.Itoa(p.Page-1)) } class="page-link">Previous</a>
|
||||||
|
|||||||
@ -216,14 +216,14 @@ func pagination(p Pagination) templ.Component {
|
|||||||
templ_7745c5c3_Var11 = templ.NopComponent
|
templ_7745c5c3_Var11 = templ.NopComponent
|
||||||
}
|
}
|
||||||
ctx = templ.ClearChildren(ctx)
|
ctx = templ.ClearChildren(ctx)
|
||||||
if p.Page > 0 {
|
if p.Page > 0 && p.TotalPages > 0 {
|
||||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav aria-label=\"Page navigation\"><ul class=\"pagination justify-content-center\">")
|
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<nav aria-label=\"Page navigation\"><ul class=\"pagination justify-content-center\">")
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
return templ_7745c5c3_Err
|
return templ_7745c5c3_Err
|
||||||
}
|
}
|
||||||
var templ_7745c5c3_Var12 = []any{
|
var templ_7745c5c3_Var12 = []any{
|
||||||
"page-item",
|
"page-item",
|
||||||
templ.KV("disabled", p.Page > 0 && p.Page == 1),
|
templ.KV("disabled", p.Page == 1),
|
||||||
}
|
}
|
||||||
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var12...)
|
||||||
if templ_7745c5c3_Err != nil {
|
if templ_7745c5c3_Err != nil {
|
||||||
|
|||||||
@ -45,6 +45,29 @@ type stats struct {
|
|||||||
CategoriesCount string
|
CategoriesCount string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NameIDPair struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortingItem struct {
|
||||||
|
NameIDPair
|
||||||
|
|
||||||
|
Ascending bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortingView struct {
|
||||||
|
Items []SortingItem
|
||||||
|
}
|
||||||
|
|
||||||
|
type CoursesFilterViewParams struct {
|
||||||
|
SelectedSchoolID string
|
||||||
|
Schools []NameIDPair
|
||||||
|
OrderBy string
|
||||||
|
OrderFields []NameIDPair
|
||||||
|
Ascending bool
|
||||||
|
}
|
||||||
|
|
||||||
type Category struct {
|
type Category struct {
|
||||||
ID string
|
ID string
|
||||||
Name string
|
Name string
|
||||||
@ -62,8 +85,10 @@ type BreadcrumbsParams struct {
|
|||||||
type FilterFormParams struct {
|
type FilterFormParams struct {
|
||||||
BreadcrumbsParams
|
BreadcrumbsParams
|
||||||
|
|
||||||
|
Schools CoursesFilterViewParams
|
||||||
AvailableLearningTypes []Category
|
AvailableLearningTypes []Category
|
||||||
AvailableCourseThematics []Category
|
AvailableCourseThematics []Category
|
||||||
|
Render bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CourseInfo struct {
|
type CourseInfo struct {
|
||||||
@ -98,6 +123,7 @@ type ListCoursesParams struct {
|
|||||||
Categories []CategoryContainer
|
Categories []CategoryContainer
|
||||||
Pagination Pagination
|
Pagination Pagination
|
||||||
Items int
|
Items int
|
||||||
|
Courses []CourseInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOrFallback[T comparable](value T, fallback T) T {
|
func GetOrFallback[T comparable](value T, fallback T) T {
|
||||||
|
|||||||
@ -2,9 +2,11 @@ package http
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"slices"
|
"slices"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"git.loyso.art/frx/kurious/internal/common/xslices"
|
"git.loyso.art/frx/kurious/internal/common/xslices"
|
||||||
"git.loyso.art/frx/kurious/internal/kurious/app/query"
|
"git.loyso.art/frx/kurious/internal/kurious/app/query"
|
||||||
@ -33,7 +35,9 @@ func makeTemplListCoursesParams(counts map[string]domain.LearningTypeStat, in ..
|
|||||||
coursesBySubcategory := make(map[string][]bootstrap.CourseInfo, len(in))
|
coursesBySubcategory := make(map[string][]bootstrap.CourseInfo, len(in))
|
||||||
subcategoriesByCategories := make(map[string]map[string]struct{}, len(in))
|
subcategoriesByCategories := make(map[string]map[string]struct{}, len(in))
|
||||||
categoryByID := make(map[string]bootstrap.CategoryBaseInfo, len(in))
|
categoryByID := make(map[string]bootstrap.CategoryBaseInfo, len(in))
|
||||||
|
seenCourses := make(map[string]struct{}, len(in))
|
||||||
|
|
||||||
|
var out bootstrap.ListCoursesParams
|
||||||
xslices.ForEach(in, func(c domain.Course) {
|
xslices.ForEach(in, func(c domain.Course) {
|
||||||
courseInfo := bootstrap.CourseInfo{
|
courseInfo := bootstrap.CourseInfo{
|
||||||
ID: c.ID,
|
ID: c.ID,
|
||||||
@ -63,9 +67,15 @@ func makeTemplListCoursesParams(counts map[string]domain.LearningTypeStat, in ..
|
|||||||
Count: counts[c.LearningTypeID].CourseThematic[c.ThematicID],
|
Count: counts[c.LearningTypeID].CourseThematic[c.ThematicID],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := seenCourses[c.ExternalID.Value()]; ok && c.ExternalID.Valid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
out.Courses = append(out.Courses, courseInfo)
|
||||||
|
seenCourses[c.ExternalID.Value()] = struct{}{}
|
||||||
})
|
})
|
||||||
|
|
||||||
var out bootstrap.ListCoursesParams
|
|
||||||
for categoryID, subcategoriesID := range subcategoriesByCategories {
|
for categoryID, subcategoriesID := range subcategoriesByCategories {
|
||||||
outCategory := bootstrap.CategoryContainer{
|
outCategory := bootstrap.CategoryContainer{
|
||||||
CategoryBaseInfo: categoryByID[categoryID],
|
CategoryBaseInfo: categoryByID[categoryID],
|
||||||
@ -112,6 +122,9 @@ func (c courseTemplServer) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
listCoursesResult, err := c.app.Queries.ListCourses.Handle(ctx, query.ListCourse{
|
listCoursesResult, err := c.app.Queries.ListCourses.Handle(ctx, query.ListCourse{
|
||||||
CourseThematic: pathParams.CourseThematic,
|
CourseThematic: pathParams.CourseThematic,
|
||||||
LearningType: pathParams.LearningType,
|
LearningType: pathParams.LearningType,
|
||||||
|
OrganizationID: pathParams.School,
|
||||||
|
OrderBy: orderByListing.getField(pathParams.OrderBy),
|
||||||
|
Ascending: pathParams.Ascending,
|
||||||
Limit: pathParams.PerPage,
|
Limit: pathParams.PerPage,
|
||||||
NextPageToken: pathParams.NextPageToken,
|
NextPageToken: pathParams.NextPageToken,
|
||||||
Offset: offset,
|
Offset: offset,
|
||||||
@ -124,6 +137,7 @@ func (c courseTemplServer) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
if handleError(ctx, err, w, c.log, "unable to load stats") {
|
if handleError(ctx, err, w, c.log, "unable to load stats") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
params := makeTemplListCoursesParams(statsresult.StatsByLearningType, listCoursesResult.Courses...)
|
params := makeTemplListCoursesParams(statsresult.StatsByLearningType, listCoursesResult.Courses...)
|
||||||
|
|
||||||
learningTypeResult, err := c.app.Queries.ListLearningTypes.Handle(ctx, query.ListLearningTypes{})
|
learningTypeResult, err := c.app.Queries.ListLearningTypes.Handle(ctx, query.ListLearningTypes{})
|
||||||
@ -164,15 +178,50 @@ func (c courseTemplServer) List(w http.ResponseWriter, r *http.Request) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
organizaions, err := c.app.Queries.ListOrganizationsStats.Handle(ctx, query.ListOrganizationsStats{})
|
||||||
|
if handleError(ctx, err, w, c.log, "unable to list organizations") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slices.SortFunc(organizaions, func(a, b domain.OrganizationStat) int {
|
||||||
|
if a.CoursesCount > b.CoursesCount {
|
||||||
|
return -1
|
||||||
|
} else if a.CoursesCount < b.CoursesCount {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.ID > b.ID {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
})
|
||||||
|
|
||||||
|
schools := xslices.Map(organizaions, func(in domain.OrganizationStat) bootstrap.NameIDPair {
|
||||||
|
return bootstrap.NameIDPair{
|
||||||
|
ID: in.ID,
|
||||||
|
Name: fmt.Sprintf("%s (count: %d)", in.Name, in.CoursesCount),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
params = bootstrap.ListCoursesParams{
|
params = bootstrap.ListCoursesParams{
|
||||||
FilterForm: bootstrap.FilterFormParams{
|
FilterForm: bootstrap.FilterFormParams{
|
||||||
|
Render: true,
|
||||||
BreadcrumbsParams: bootstrap.BreadcrumbsParams{
|
BreadcrumbsParams: bootstrap.BreadcrumbsParams{
|
||||||
ActiveLearningType: params.FilterForm.ActiveLearningType,
|
ActiveLearningType: params.FilterForm.ActiveLearningType,
|
||||||
ActiveCourseThematic: params.FilterForm.ActiveCourseThematic,
|
ActiveCourseThematic: params.FilterForm.ActiveCourseThematic,
|
||||||
},
|
},
|
||||||
AvailableLearningTypes: params.FilterForm.AvailableLearningTypes,
|
AvailableLearningTypes: params.FilterForm.AvailableLearningTypes,
|
||||||
AvailableCourseThematics: params.FilterForm.AvailableCourseThematics,
|
AvailableCourseThematics: params.FilterForm.AvailableCourseThematics,
|
||||||
|
Schools: bootstrap.CoursesFilterViewParams{
|
||||||
|
SelectedSchoolID: pathParams.School,
|
||||||
|
Schools: schools,
|
||||||
|
Ascending: pathParams.Ascending,
|
||||||
|
OrderBy: pathParams.OrderBy,
|
||||||
|
OrderFields: orderByListing.asNameIDPair(),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
Courses: params.Courses,
|
||||||
Categories: params.Categories,
|
Categories: params.Categories,
|
||||||
Pagination: bootstrap.Pagination{
|
Pagination: bootstrap.Pagination{
|
||||||
Page: pathParams.Page,
|
Page: pathParams.Page,
|
||||||
@ -279,3 +328,65 @@ func (c courseTemplServer) Index(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
span.SetStatus(codes.Ok, "request completed")
|
span.SetStatus(codes.Ok, "request completed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var orderByListing = newOrderableContainer(
|
||||||
|
newOrderableUnit("pr", "Price", "full_price"),
|
||||||
|
newOrderableUnit("na", "Name", "name"),
|
||||||
|
newOrderableUnit("di", "Discount", "discount"),
|
||||||
|
newOrderableUnit("du", "Duration", "duration"),
|
||||||
|
newOrderableUnit("st", "Starts At", "starts_at"),
|
||||||
|
)
|
||||||
|
|
||||||
|
type orderableUnit struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Field string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOrderableUnit(id, name, field string) orderableUnit {
|
||||||
|
return orderableUnit{
|
||||||
|
ID: id,
|
||||||
|
Name: name,
|
||||||
|
Field: field,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type orderableContainer struct {
|
||||||
|
nameByID map[string]string
|
||||||
|
fieldByID map[string]string
|
||||||
|
cachedNameIDPair []bootstrap.NameIDPair
|
||||||
|
makeCache sync.Once
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *orderableContainer) asNameIDPair() []bootstrap.NameIDPair {
|
||||||
|
c.makeCache.Do(func() {
|
||||||
|
c.cachedNameIDPair = make([]bootstrap.NameIDPair, 0, len(c.nameByID))
|
||||||
|
for id, name := range c.nameByID {
|
||||||
|
c.cachedNameIDPair = append(c.cachedNameIDPair, bootstrap.NameIDPair{
|
||||||
|
ID: id,
|
||||||
|
Name: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return c.cachedNameIDPair
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *orderableContainer) getField(id string) string {
|
||||||
|
return c.fieldByID[id]
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOrderableContainer(units ...orderableUnit) *orderableContainer {
|
||||||
|
nameByID := make(map[string]string, len(units))
|
||||||
|
fieldByID := make(map[string]string, len(units))
|
||||||
|
|
||||||
|
xslices.ForEach(units, func(u orderableUnit) {
|
||||||
|
nameByID[u.ID] = u.Name
|
||||||
|
fieldByID[u.ID] = u.Field
|
||||||
|
})
|
||||||
|
|
||||||
|
return &orderableContainer{
|
||||||
|
nameByID: nameByID,
|
||||||
|
fieldByID: fieldByID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"git.loyso.art/frx/kurious/internal/common/errors"
|
"git.loyso.art/frx/kurious/internal/common/errors"
|
||||||
"git.loyso.art/frx/kurious/internal/common/xcontext"
|
"git.loyso.art/frx/kurious/internal/common/xcontext"
|
||||||
"git.loyso.art/frx/kurious/internal/kurious/service"
|
"git.loyso.art/frx/kurious/internal/kurious/service"
|
||||||
|
"git.loyso.art/frx/kurious/pkg/xdefault"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
@ -84,7 +85,7 @@ func parsePaginationFromQuery(r *http.Request) (out pagination, err error) {
|
|||||||
return out, errors.NewValidationError("per_page", "bad per_page value")
|
return out, errors.NewValidationError("per_page", "bad per_page value")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
out.PerPage = 50
|
out.PerPage = 20
|
||||||
}
|
}
|
||||||
if query.Has("page") {
|
if query.Has("page") {
|
||||||
out.Page, err = strconv.Atoi(query.Get("page"))
|
out.Page, err = strconv.Atoi(query.Get("page"))
|
||||||
@ -113,6 +114,13 @@ func parseListCoursesParams(r *http.Request) (out listCoursesParams, err error)
|
|||||||
out.LearningType = vars[LearningTypePathParam]
|
out.LearningType = vars[LearningTypePathParam]
|
||||||
out.CourseThematic = vars[ThematicTypePathParam]
|
out.CourseThematic = vars[ThematicTypePathParam]
|
||||||
|
|
||||||
|
out.School = r.URL.Query().Get("school_id")
|
||||||
|
out.OrderBy = xdefault.WithFallback(r.URL.Query().Get("order_by"), "price")
|
||||||
|
|
||||||
|
if r.URL.Query().Has("asc") {
|
||||||
|
out.Ascending, _ = strconv.ParseBool(r.URL.Query().Get("asc"))
|
||||||
|
}
|
||||||
|
|
||||||
return out, nil
|
return out, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,6 +129,9 @@ type listCoursesParams struct {
|
|||||||
|
|
||||||
CourseThematic string
|
CourseThematic string
|
||||||
LearningType string
|
LearningType string
|
||||||
|
School string
|
||||||
|
OrderBy string
|
||||||
|
Ascending bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type IDNamePair struct {
|
type IDNamePair struct {
|
||||||
|
|||||||
@ -89,6 +89,7 @@ func NewApplication(ctx context.Context, cfg ApplicationConfig, mapper domain.Co
|
|||||||
GetCourse: query.NewGetCourseHandler(courseadapter, mapper, log),
|
GetCourse: query.NewGetCourseHandler(courseadapter, mapper, log),
|
||||||
|
|
||||||
ListOrganzations: query.NewListOrganizationsHandler(organizationrepo, log),
|
ListOrganzations: query.NewListOrganizationsHandler(organizationrepo, log),
|
||||||
|
ListOrganizationsStats: query.NewListOrganizationsStatsHandler(organizationrepo, log),
|
||||||
GetOrganization: query.NewGetOrganizationHandler(organizationrepo, log),
|
GetOrganization: query.NewGetOrganizationHandler(organizationrepo, log),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user