Compare commits
117 Commits
main
...
457accfbfa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
457accfbfa | ||
|
|
43287a4baa | ||
|
|
93fea8ecad | ||
|
|
ba7b0322ff | ||
|
|
473686528e | ||
| c1b47674c8 | |||
|
|
85f234b05e | ||
| 50fa4ed9a5 | |||
|
|
07496e6673 | ||
|
|
9045a8c017 | ||
|
|
3e314a9b9a | ||
| bc4716c48f | |||
|
|
0e69759549 | ||
|
|
88ea7e70da | ||
|
|
7a30f9a29b | ||
| 83fa1ea020 | |||
|
|
4544911f0e | ||
|
|
2aaa91444d | ||
| 4d3867614d | |||
|
|
c18db76c7a | ||
|
|
78df95e985 | ||
| 6738db8c60 | |||
|
|
8acb81e6b4 | ||
|
|
91125e82f5 | ||
|
|
e9bd759fa5 | ||
|
|
57416bf53a | ||
|
|
0b6041d146 | ||
|
|
900e6146e7 | ||
|
|
e905052cc1 | ||
|
|
c1d910708a | ||
| b8abf224cf | |||
|
|
b47f00651e | ||
| a9c74d2f9c | |||
|
|
d1b5bea1bc | ||
|
|
ab22ec86ec | ||
|
|
65295236c5 | ||
|
|
f104161bbd | ||
|
|
844dac206c | ||
| be48b4b295 | |||
|
|
1059d767d5 | ||
|
|
7be776f189 | ||
|
|
41a217e8a9 | ||
| bb48d9c164 | |||
|
|
01688cf042 | ||
|
|
b97eb9c8a4 | ||
|
|
c089215835 | ||
|
|
546a020003 | ||
| 38b2294aad | |||
|
|
c9ecdd2c77 | ||
|
|
21d0c27f4d | ||
|
|
20d6018836 | ||
| 7bfa3b390e | |||
|
|
4658b168ed | ||
|
|
6ad4fc9ee9 | ||
|
|
5f2a7fd494 | ||
|
|
0fd72ea1de | ||
| 0aecbfa3dd | |||
|
|
62b7a75093 | ||
|
|
07408dbbbe | ||
| 23e4218f6f | |||
|
|
34cb63364c | ||
|
|
28fd51f926 | ||
|
|
a5e39c94c5 | ||
|
|
4e327b7318 | ||
| c69ea82f26 | |||
|
|
a4075866f8 | ||
|
|
5b53a10bbc | ||
|
|
585e7d2226 | ||
| a1dbab98be | |||
|
|
e0d755bef7 | ||
|
|
a075f13587 | ||
|
|
7fe1eef297 | ||
|
|
20b70c06c1 | ||
| 4588e63fa5 | |||
|
|
400f67b268 | ||
|
|
ea8e3f6a24 | ||
|
|
32c436e772 | ||
|
|
a1ff9f2831 | ||
|
|
b3f1b72da4 | ||
|
|
aabd9a8266 | ||
|
|
f98a0c2c02 | ||
| 6dc8cae3a8 | |||
|
|
34a24ce6c9 | ||
|
|
e5002ce440 | ||
|
|
f158845279 | ||
|
|
bd30b32b49 | ||
|
|
e2299c6d46 | ||
|
|
62d28734fc | ||
|
|
b7c303377a | ||
|
|
1d09716492 | ||
|
|
c409d44765 | ||
|
|
2b95ab37f6 | ||
|
|
ff42f3cd60 | ||
|
|
43df46f520 | ||
|
|
70273f6760 | ||
|
|
566291fb43 | ||
|
|
91dfdce66d | ||
|
|
8210a4bd9b | ||
|
|
839e316226 | ||
|
|
c2f5c0d97c | ||
|
|
1911d5623c | ||
|
|
9ee98a4cb4 | ||
|
|
77004b14b0 | ||
|
|
2d86881f94 | ||
|
|
4dcaae7136 | ||
|
|
3cba2fa485 | ||
|
|
ae90562d88 | ||
|
|
7ca74970fa | ||
|
|
aa2fe41ecc | ||
|
|
11dd12682c | ||
|
|
e58fa6efa2 | ||
|
|
9f14b74936 | ||
|
|
063991770a | ||
|
|
2a0558347a | ||
|
|
58c5ce339b | ||
|
|
30ac0d569a | ||
|
|
8a01606c80 |
391
.agents/skills/interface-design/SKILL.md
Normal file
391
.agents/skills/interface-design/SKILL.md
Normal file
@@ -0,0 +1,391 @@
|
|||||||
|
---
|
||||||
|
name: interface-design
|
||||||
|
description: This skill is for interface design — dashboards, admin panels, apps, tools, and interactive products. NOT for marketing design (landing pages, marketing sites, campaigns).
|
||||||
|
---
|
||||||
|
|
||||||
|
# Interface Design
|
||||||
|
|
||||||
|
Build interface design with craft and consistency.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
**Use for:** Dashboards, admin panels, SaaS apps, tools, settings pages, data interfaces.
|
||||||
|
|
||||||
|
**Not for:** Landing pages, marketing sites, campaigns. Redirect those to `/frontend-design`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# The Problem
|
||||||
|
|
||||||
|
You will generate generic output. Your training has seen thousands of dashboards. The patterns are strong.
|
||||||
|
|
||||||
|
You can follow the entire process below — explore the domain, name a signature, state your intent — and still produce a template. Warm colors on cold structures. Friendly fonts on generic layouts. "Kitchen feel" that looks like every other app.
|
||||||
|
|
||||||
|
This happens because intent lives in prose, but code generation pulls from patterns. The gap between them is where defaults win.
|
||||||
|
|
||||||
|
The process below helps. But process alone doesn't guarantee craft. You have to catch yourself.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Where Defaults Hide
|
||||||
|
|
||||||
|
Defaults don't announce themselves. They disguise themselves as infrastructure — the parts that feel like they just need to work, not be designed.
|
||||||
|
|
||||||
|
**Typography feels like a container.** Pick something readable, move on. But typography isn't holding your design — it IS your design. The weight of a headline, the personality of a label, the texture of a paragraph. These shape how the product feels before anyone reads a word. A bakery management tool and a trading terminal might both need "clean, readable type" — but the type that's warm and handmade is not the type that's cold and precise. If you're reaching for your usual font, you're not designing.
|
||||||
|
|
||||||
|
**Navigation feels like scaffolding.** Build the sidebar, add the links, get to the real work. But navigation isn't around your product — it IS your product. Where you are, where you can go, what matters most. A page floating in space is a component demo, not software. The navigation teaches people how to think about the space they're in.
|
||||||
|
|
||||||
|
**Data feels like presentation.** You have numbers, show numbers. But a number on screen is not design. The question is: what does this number mean to the person looking at it? What will they do with it? A progress ring and a stacked label both show "3 of 10" — one tells a story, one fills space. If you're reaching for number-on-label, you're not designing.
|
||||||
|
|
||||||
|
**Token names feel like implementation detail.** But your CSS variables are design decisions. `--ink` and `--parchment` evoke a world. `--gray-700` and `--surface-2` evoke a template. Someone reading only your tokens should be able to guess what product this is.
|
||||||
|
|
||||||
|
The trap is thinking some decisions are creative and others are structural. There are no structural decisions. Everything is design. The moment you stop asking "why this?" is the moment defaults take over.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Intent First
|
||||||
|
|
||||||
|
Before touching code, answer these. Not in your head — out loud, to yourself or the user.
|
||||||
|
|
||||||
|
**Who is this human?**
|
||||||
|
Not "users." The actual person. Where are they when they open this? What's on their mind? What did they do 5 minutes ago, what will they do 5 minutes after? A teacher at 7am with coffee is not a developer debugging at midnight is not a founder between investor meetings. Their world shapes the interface.
|
||||||
|
|
||||||
|
**What must they accomplish?**
|
||||||
|
Not "use the dashboard." The verb. Grade these submissions. Find the broken deployment. Approve the payment. The answer determines what leads, what follows, what hides.
|
||||||
|
|
||||||
|
**What should this feel like?**
|
||||||
|
Say it in words that mean something. "Clean and modern" means nothing — every AI says that. Warm like a notebook? Cold like a terminal? Dense like a trading floor? Calm like a reading app? The answer shapes color, type, spacing, density — everything.
|
||||||
|
|
||||||
|
If you cannot answer these with specifics, stop. Ask the user. Do not guess. Do not default.
|
||||||
|
|
||||||
|
## Every Choice Must Be A Choice
|
||||||
|
|
||||||
|
For every decision, you must be able to explain WHY.
|
||||||
|
|
||||||
|
- Why this layout and not another?
|
||||||
|
- Why this color temperature?
|
||||||
|
- Why this typeface?
|
||||||
|
- Why this spacing scale?
|
||||||
|
- Why this information hierarchy?
|
||||||
|
|
||||||
|
If your answer is "it's common" or "it's clean" or "it works" — you haven't chosen. You've defaulted. Defaults are invisible. Invisible choices compound into generic output.
|
||||||
|
|
||||||
|
**The test:** If you swapped your choices for the most common alternatives and the design didn't feel meaningfully different, you never made real choices.
|
||||||
|
|
||||||
|
## Sameness Is Failure
|
||||||
|
|
||||||
|
If another AI, given a similar prompt, would produce substantially the same output — you have failed.
|
||||||
|
|
||||||
|
This is not about being different for its own sake. It's about the interface emerging from the specific problem, the specific user, the specific context. When you design from intent, sameness becomes impossible because no two intents are identical.
|
||||||
|
|
||||||
|
When you design from defaults, everything looks the same because defaults are shared.
|
||||||
|
|
||||||
|
## Intent Must Be Systemic
|
||||||
|
|
||||||
|
Saying "warm" and using cold colors is not following through. Intent is not a label — it's a constraint that shapes every decision.
|
||||||
|
|
||||||
|
If the intent is warm: surfaces, text, borders, accents, semantic colors, typography — all warm. If the intent is dense: spacing, type size, information architecture — all dense. If the intent is calm: motion, contrast, color saturation — all calm.
|
||||||
|
|
||||||
|
Check your output against your stated intent. Does every token reinforce it? Or did you state an intent and then default anyway?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Product Domain Exploration
|
||||||
|
|
||||||
|
This is where defaults get caught — or don't.
|
||||||
|
|
||||||
|
Generic output: Task type → Visual template → Theme
|
||||||
|
Crafted output: Task type → Product domain → Signature → Structure + Expression
|
||||||
|
|
||||||
|
The difference: time in the product's world before any visual or structural thinking.
|
||||||
|
|
||||||
|
## Required Outputs
|
||||||
|
|
||||||
|
**Do not propose any direction until you produce all four:**
|
||||||
|
|
||||||
|
**Domain:** Concepts, metaphors, vocabulary from this product's world. Not features — territory. Minimum 5.
|
||||||
|
|
||||||
|
**Color world:** What colors exist naturally in this product's domain? Not "warm" or "cool" — go to the actual world. If this product were a physical space, what would you see? What colors belong there that don't belong elsewhere? List 5+.
|
||||||
|
|
||||||
|
**Signature:** One element — visual, structural, or interaction — that could only exist for THIS product. If you can't name one, keep exploring.
|
||||||
|
|
||||||
|
**Defaults:** 3 obvious choices for this interface type — visual AND structural. You can't avoid patterns you haven't named.
|
||||||
|
|
||||||
|
## Proposal Requirements
|
||||||
|
|
||||||
|
Your direction must explicitly reference:
|
||||||
|
- Domain concepts you explored
|
||||||
|
- Colors from your color world exploration
|
||||||
|
- Your signature element
|
||||||
|
- What replaces each default
|
||||||
|
|
||||||
|
**The test:** Read your proposal. Remove the product name. Could someone identify what this is for? If not, it's generic. Explore deeper.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# The Mandate
|
||||||
|
|
||||||
|
**Before showing the user, look at what you made.**
|
||||||
|
|
||||||
|
Ask yourself: "If they said this lacks craft, what would they mean?"
|
||||||
|
|
||||||
|
That thing you just thought of — fix it first.
|
||||||
|
|
||||||
|
Your first output is probably generic. That's normal. The work is catching it before the user has to.
|
||||||
|
|
||||||
|
## The Checks
|
||||||
|
|
||||||
|
Run these against your output before presenting:
|
||||||
|
|
||||||
|
- **The swap test:** If you swapped the typeface for your usual one, would anyone notice? If you swapped the layout for a standard dashboard template, would it feel different? The places where swapping wouldn't matter are the places you defaulted.
|
||||||
|
|
||||||
|
- **The squint test:** Blur your eyes. Can you still perceive hierarchy? Is anything jumping out harshly? Craft whispers.
|
||||||
|
|
||||||
|
- **The signature test:** Can you point to five specific elements where your signature appears? Not "the overall feel" — actual components. A signature you can't locate doesn't exist.
|
||||||
|
|
||||||
|
- **The token test:** Read your CSS variables out loud. Do they sound like they belong to this product's world, or could they belong to any project?
|
||||||
|
|
||||||
|
If any check fails, iterate before showing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Craft Foundations
|
||||||
|
|
||||||
|
## Subtle Layering
|
||||||
|
|
||||||
|
This is the backbone of craft. Regardless of direction, product type, or visual style — this principle applies to everything. You should barely notice the system working. When you look at Vercel's dashboard, you don't think "nice borders." You just understand the structure. The craft is invisible — that's how you know it's working.
|
||||||
|
|
||||||
|
### Surface Elevation
|
||||||
|
|
||||||
|
Surfaces stack. A dropdown sits above a card which sits above the page. Build a numbered system — base, then increasing elevation levels. In dark mode, higher elevation = slightly lighter. In light mode, higher elevation = slightly lighter or uses shadow.
|
||||||
|
|
||||||
|
Each jump should be only a few percentage points of lightness. You can barely see the difference in isolation. But when surfaces stack, the hierarchy emerges. Whisper-quiet shifts that you feel rather than see.
|
||||||
|
|
||||||
|
**Key decisions:**
|
||||||
|
- **Sidebars:** Same background as canvas, not different. Different colors fragment the visual space into "sidebar world" and "content world." A subtle border is enough separation.
|
||||||
|
- **Dropdowns:** One level above their parent surface. If both share the same level, the dropdown blends into the card and layering is lost.
|
||||||
|
- **Inputs:** Slightly darker than their surroundings, not lighter. Inputs are "inset" — they receive content. A darker background signals "type here" without heavy borders.
|
||||||
|
|
||||||
|
### Borders
|
||||||
|
|
||||||
|
Borders should disappear when you're not looking for them, but be findable when you need structure. Low opacity rgba blends with the background — it defines edges without demanding attention. Solid hex borders look harsh in comparison.
|
||||||
|
|
||||||
|
Build a progression — not all borders are equal. Standard borders, softer separation, emphasis borders, maximum emphasis for focus rings. Match intensity to the importance of the boundary.
|
||||||
|
|
||||||
|
**The squint test:** Blur your eyes at the interface. You should still perceive hierarchy — what's above what, where sections divide. But nothing should jump out. No harsh lines. No jarring color shifts. Just quiet structure.
|
||||||
|
|
||||||
|
This separates professional interfaces from amateur ones. Get this wrong and nothing else matters.
|
||||||
|
|
||||||
|
## Infinite Expression
|
||||||
|
|
||||||
|
Every pattern has infinite expressions. **No interface should look the same.**
|
||||||
|
|
||||||
|
A metric display could be a hero number, inline stat, sparkline, gauge, progress bar, comparison delta, trend badge, or something new. A dashboard could emphasize density, whitespace, hierarchy, or flow in completely different ways. Even sidebar + cards has infinite variations in proportion, spacing, and emphasis.
|
||||||
|
|
||||||
|
**Before building, ask:**
|
||||||
|
- What's the ONE thing users do most here?
|
||||||
|
- What products solve similar problems brilliantly? Study them.
|
||||||
|
- Why would this interface feel designed for its purpose, not templated?
|
||||||
|
|
||||||
|
**NEVER produce identical output.** Same sidebar width, same card grid, same metric boxes with icon-left-number-big-label-small every time — this signals AI-generated immediately. It's forgettable.
|
||||||
|
|
||||||
|
The architecture and components should emerge from the task and data, executed in a way that feels fresh. Linear's cards don't look like Notion's. Vercel's metrics don't look like Stripe's. Same concepts, infinite expressions.
|
||||||
|
|
||||||
|
## Color Lives Somewhere
|
||||||
|
|
||||||
|
Every product exists in a world. That world has colors.
|
||||||
|
|
||||||
|
Before you reach for a palette, spend time in the product's world. What would you see if you walked into the physical version of this space? What materials? What light? What objects?
|
||||||
|
|
||||||
|
Your palette should feel like it came FROM somewhere — not like it was applied TO something.
|
||||||
|
|
||||||
|
**Beyond Warm and Cold:** Temperature is one axis. Is this quiet or loud? Dense or spacious? Serious or playful? Geometric or organic? A trading terminal and a meditation app are both "focused" — completely different kinds of focus. Find the specific quality, not the generic label.
|
||||||
|
|
||||||
|
**Color Carries Meaning:** Gray builds structure. Color communicates — status, action, emphasis, identity. Unmotivated color is noise. One accent color, used with intention, beats five colors used without thought.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Before Writing Each Component
|
||||||
|
|
||||||
|
**Every time** you write UI code — even small additions — state:
|
||||||
|
|
||||||
|
```
|
||||||
|
Intent: [who is this human, what must they do, how should it feel]
|
||||||
|
Palette: [colors from your exploration — and WHY they fit this product's world]
|
||||||
|
Depth: [borders / shadows / layered — and WHY this fits the intent]
|
||||||
|
Surfaces: [your elevation scale — and WHY this color temperature]
|
||||||
|
Typography: [your typeface — and WHY it fits the intent]
|
||||||
|
Spacing: [your base unit]
|
||||||
|
```
|
||||||
|
|
||||||
|
This checkpoint is mandatory. It forces you to connect every technical choice back to intent.
|
||||||
|
|
||||||
|
If you can't explain WHY for each choice, you're defaulting. Stop and think.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Design Principles
|
||||||
|
|
||||||
|
## Token Architecture
|
||||||
|
|
||||||
|
Every color in your interface should trace back to a small set of primitives: foreground (text hierarchy), background (surface elevation), border (separation hierarchy), brand, and semantic (destructive, warning, success). No random hex values — everything maps to primitives.
|
||||||
|
|
||||||
|
### Text Hierarchy
|
||||||
|
|
||||||
|
Don't just have "text" and "gray text." Build four levels — primary, secondary, tertiary, muted. Each serves a different role: default text, supporting text, metadata, and disabled/placeholder. Use all four consistently. If you're only using two, your hierarchy is too flat.
|
||||||
|
|
||||||
|
### Border Progression
|
||||||
|
|
||||||
|
Borders aren't binary. Build a scale that matches intensity to importance — standard separation, softer separation, emphasis, maximum emphasis. Not every boundary deserves the same weight.
|
||||||
|
|
||||||
|
### Control Tokens
|
||||||
|
|
||||||
|
Form controls have specific needs. Don't reuse surface tokens — create dedicated ones for control backgrounds, control borders, and focus states. This lets you tune interactive elements independently from layout surfaces.
|
||||||
|
|
||||||
|
## Spacing
|
||||||
|
|
||||||
|
Pick a base unit and stick to multiples. Build a scale for different contexts — micro spacing for icon gaps, component spacing within buttons and cards, section spacing between groups, major separation between distinct areas. Random values signal no system.
|
||||||
|
|
||||||
|
## Padding
|
||||||
|
|
||||||
|
Keep it symmetrical. If one side has a value, others should match unless content naturally requires asymmetry.
|
||||||
|
|
||||||
|
## Depth
|
||||||
|
|
||||||
|
Choose ONE approach and commit:
|
||||||
|
- **Borders-only** — Clean, technical. For dense tools.
|
||||||
|
- **Subtle shadows** — Soft lift. For approachable products.
|
||||||
|
- **Layered shadows** — Premium, dimensional. For cards that need presence.
|
||||||
|
- **Surface color shifts** — Background tints establish hierarchy without shadows.
|
||||||
|
|
||||||
|
Don't mix approaches.
|
||||||
|
|
||||||
|
## Border Radius
|
||||||
|
|
||||||
|
Sharper feels technical. Rounder feels friendly. Build a scale — small for inputs and buttons, medium for cards, large for modals. Don't mix sharp and soft randomly.
|
||||||
|
|
||||||
|
## Typography
|
||||||
|
|
||||||
|
Build distinct levels distinguishable at a glance. Headlines need weight and tight tracking for presence. Body needs comfortable weight for readability. Labels need medium weight that works at smaller sizes. Data needs monospace with tabular number spacing for alignment. Don't rely on size alone — combine size, weight, and letter-spacing.
|
||||||
|
|
||||||
|
## Card Layouts
|
||||||
|
|
||||||
|
A metric card doesn't have to look like a plan card doesn't have to look like a settings card. Design each card's internal structure for its specific content — but keep the surface treatment consistent: same border weight, shadow depth, corner radius, padding scale.
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
|
||||||
|
Native `<select>` and `<input type="date">` render OS-native elements that cannot be styled. Build custom components — trigger buttons with positioned dropdowns, calendar popovers, styled state management.
|
||||||
|
|
||||||
|
## Iconography
|
||||||
|
|
||||||
|
Icons clarify, not decorate — if removing an icon loses no meaning, remove it. Choose one icon set and stick with it. Give standalone icons presence with subtle background containers.
|
||||||
|
|
||||||
|
## Animation
|
||||||
|
|
||||||
|
Fast micro-interactions, smooth easing. Larger transitions can be slightly longer. Use deceleration easing. Avoid spring/bounce in professional interfaces.
|
||||||
|
|
||||||
|
## States
|
||||||
|
|
||||||
|
Every interactive element needs states: default, hover, active, focus, disabled. Data needs states too: loading, empty, error. Missing states feel broken.
|
||||||
|
|
||||||
|
## Navigation Context
|
||||||
|
|
||||||
|
Screens need grounding. A data table floating in space feels like a component demo, not a product. Include navigation showing where you are in the app, location indicators, and user context. When building sidebars, consider same background as main content with border separation rather than different colors.
|
||||||
|
|
||||||
|
## Dark Mode
|
||||||
|
|
||||||
|
Dark interfaces have different needs. Shadows are less visible on dark backgrounds — lean on borders for definition. Semantic colors (success, warning, error) often need slight desaturation. The hierarchy system still applies, just with inverted values.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Avoid
|
||||||
|
|
||||||
|
- **Harsh borders** — if borders are the first thing you see, they're too strong
|
||||||
|
- **Dramatic surface jumps** — elevation changes should be whisper-quiet
|
||||||
|
- **Inconsistent spacing** — the clearest sign of no system
|
||||||
|
- **Mixed depth strategies** — pick one approach and commit
|
||||||
|
- **Missing interaction states** — hover, focus, disabled, loading, error
|
||||||
|
- **Dramatic drop shadows** — shadows should be subtle, not attention-grabbing
|
||||||
|
- **Large radius on small elements**
|
||||||
|
- **Pure white cards on colored backgrounds**
|
||||||
|
- **Thick decorative borders**
|
||||||
|
- **Gradients and color for decoration** — color should mean something
|
||||||
|
- **Multiple accent colors** — dilutes focus
|
||||||
|
- **Different hues for different surfaces** — keep the same hue, shift only lightness
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Workflow
|
||||||
|
|
||||||
|
## Communication
|
||||||
|
Be invisible. Don't announce modes or narrate process.
|
||||||
|
|
||||||
|
**Never say:** "I'm in ESTABLISH MODE", "Let me check system.md..."
|
||||||
|
|
||||||
|
**Instead:** Jump into work. State suggestions with reasoning.
|
||||||
|
|
||||||
|
## Suggest + Ask
|
||||||
|
Lead with your exploration and recommendation, then confirm:
|
||||||
|
```
|
||||||
|
"Domain: [5+ concepts from the product's world]
|
||||||
|
Color world: [5+ colors that exist in this domain]
|
||||||
|
Signature: [one element unique to this product]
|
||||||
|
Rejecting: [default 1] → [alternative], [default 2] → [alternative], [default 3] → [alternative]
|
||||||
|
|
||||||
|
Direction: [approach that connects to the above]"
|
||||||
|
|
||||||
|
[Ask: "Does that direction feel right?"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## If Project Has system.md
|
||||||
|
Read `.interface-design/system.md` and apply. Decisions are made.
|
||||||
|
|
||||||
|
## If No system.md
|
||||||
|
1. Explore domain — Produce all four required outputs
|
||||||
|
2. Propose — Direction must reference all four
|
||||||
|
3. Confirm — Get user buy-in
|
||||||
|
4. Build — Apply principles
|
||||||
|
5. **Evaluate** — Run the mandate checks before showing
|
||||||
|
6. Offer to save
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# After Completing a Task
|
||||||
|
|
||||||
|
When you finish building something, **always offer to save**:
|
||||||
|
|
||||||
|
```
|
||||||
|
"Want me to save these patterns for future sessions?"
|
||||||
|
```
|
||||||
|
|
||||||
|
If yes, write to `.interface-design/system.md`:
|
||||||
|
- Direction and feel
|
||||||
|
- Depth strategy (borders/shadows/layered)
|
||||||
|
- Spacing base unit
|
||||||
|
- Key component patterns
|
||||||
|
|
||||||
|
### What to Save
|
||||||
|
|
||||||
|
Add patterns when a component is used 2+ times, is reusable across the project, or has specific measurements worth remembering. Don't save one-off components, temporary experiments, or variations better handled with props.
|
||||||
|
|
||||||
|
### Consistency Checks
|
||||||
|
|
||||||
|
If system.md defines values, check against them: spacing on the defined grid, depth using the declared strategy throughout, colors from the defined palette, documented patterns reused instead of reinvented.
|
||||||
|
|
||||||
|
This compounds — each save makes future work faster and more consistent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Deep Dives
|
||||||
|
|
||||||
|
For more detail on specific topics:
|
||||||
|
- `references/principles.md` — Code examples, specific values, dark mode
|
||||||
|
- `references/validation.md` — Memory management, when to update system.md
|
||||||
|
- `references/critique.md` — Post-build craft critique protocol
|
||||||
|
|
||||||
|
# Commands
|
||||||
|
|
||||||
|
- `/interface-design:status` — Current system state
|
||||||
|
- `/interface-design:audit` — Check code against system
|
||||||
|
- `/interface-design:extract` — Extract patterns from code
|
||||||
|
- `/interface-design:critique` — Critique your build for craft, then rebuild what defaulted
|
||||||
67
.agents/skills/interface-design/references/critique.md
Normal file
67
.agents/skills/interface-design/references/critique.md
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Critique
|
||||||
|
|
||||||
|
Your first build shipped the structure. Now look at it the way a design lead reviews a junior's work — not asking "does this work?" but "would I put my name on this?"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Gap
|
||||||
|
|
||||||
|
There's a distance between correct and crafted. Correct means the layout holds, the grid aligns, the colors don't clash. Crafted means someone cared about every decision down to the last pixel. You can feel the difference immediately — the way you tell a hand-thrown mug from an injection-molded one. Both hold coffee. One has presence.
|
||||||
|
|
||||||
|
Your first output lives in correct. This command pulls it toward crafted.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## See the Composition
|
||||||
|
|
||||||
|
Step back. Look at the whole thing.
|
||||||
|
|
||||||
|
Does the layout have rhythm? Great interfaces breathe unevenly — dense tooling areas give way to open content, heavy elements balance against light ones, the eye travels through the page with purpose. Default layouts are monotone: same card size, same gaps, same density everywhere. Flatness is the sound of no one deciding.
|
||||||
|
|
||||||
|
Are proportions doing work? A 280px sidebar next to full-width content says "navigation serves content." A 360px sidebar says "these are peers." The specific number declares what matters. If you can't articulate what your proportions are saying, they're not saying anything.
|
||||||
|
|
||||||
|
Is there a clear focal point? Every screen has one thing the user came here to do. That thing should dominate — through size, position, contrast, or the space around it. When everything competes equally, nothing wins and the interface feels like a parking lot.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## See the Craft
|
||||||
|
|
||||||
|
Move close. Pixel-close.
|
||||||
|
|
||||||
|
The spacing grid is non-negotiable — every value a multiple of 4, no exceptions — but correctness alone isn't craft. Craft is knowing that a tool panel at 16px padding feels workbench-tight while the same card at 24px feels like a brochure. The same number can be right in one context and lazy in another. Density is a design decision, not a constant.
|
||||||
|
|
||||||
|
Typography should be legible even squinted. If size is the only thing separating your headline from your body from your label, the hierarchy is too weak. Weight, tracking, and opacity create layers that size alone can't.
|
||||||
|
|
||||||
|
Surfaces should whisper hierarchy. Not thick borders, not dramatic shadows — quiet tonal shifts where you feel the depth without seeing it. Remove every border from your CSS mentally. Can you still perceive the structure through surface color alone? If not, your surfaces aren't working hard enough.
|
||||||
|
|
||||||
|
Interactive elements need life. Every button, link, and clickable region should respond to hover and press. Not dramatically — a subtle shift in background, a gentle darkening. Missing states make an interface feel like a photograph of software instead of software.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## See the Content
|
||||||
|
|
||||||
|
Read every visible string as a user would. Not checking for typos — checking for truth.
|
||||||
|
|
||||||
|
Does this screen tell one coherent story? Could a real person at a real company be looking at exactly this data right now? Or does the page title belong to one product, the article body to another, and the sidebar metrics to a third?
|
||||||
|
|
||||||
|
Content incoherence breaks the illusion faster than any visual flaw. A beautifully designed interface with nonsensical content is a movie set with no script.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## See the Structure
|
||||||
|
|
||||||
|
Open the CSS and find the lies — the places that look right but are held together with tape.
|
||||||
|
|
||||||
|
Negative margins undoing a parent's padding. Calc() values that exist only as workarounds. Absolute positioning to escape layout flow. Each is a shortcut where a clean solution exists. Cards with full-width dividers use flex column and section-level padding. Centered content uses max-width with auto margins. The correct answer is always simpler than the hack.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Again
|
||||||
|
|
||||||
|
Look at your output one final time.
|
||||||
|
|
||||||
|
Ask: "If they said this lacks craft, what would they point to?"
|
||||||
|
|
||||||
|
That thing you just thought of — fix it. Then ask again.
|
||||||
|
|
||||||
|
The first build was the draft. The critique is the design.
|
||||||
86
.agents/skills/interface-design/references/example.md
Normal file
86
.agents/skills/interface-design/references/example.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Craft in Action
|
||||||
|
|
||||||
|
This shows how the subtle layering principle translates to real decisions. Learn the thinking, not the code. Your values will differ — the approach won't.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Subtle Layering Mindset
|
||||||
|
|
||||||
|
Before looking at any example, internalize this: **you should barely notice the system working.**
|
||||||
|
|
||||||
|
When you look at Vercel's dashboard, you don't think "nice borders." You just understand the structure. When you look at Supabase, you don't think "good surface elevation." You just know what's above what. The craft is invisible — that's how you know it's working.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example: Dashboard with Sidebar and Dropdown
|
||||||
|
|
||||||
|
### The Surface Decisions
|
||||||
|
|
||||||
|
**Why so subtle?** Each elevation jump should be only a few percentage points of lightness. You can barely see the difference in isolation. But when surfaces stack, the hierarchy emerges. This is the Vercel/Supabase way — whisper-quiet shifts that you feel rather than see.
|
||||||
|
|
||||||
|
**What NOT to do:** Don't make dramatic jumps between elevations. That's jarring. Don't use different hues for different levels. Keep the same hue, shift only lightness.
|
||||||
|
|
||||||
|
### The Border Decisions
|
||||||
|
|
||||||
|
**Why rgba, not solid colors?** Low opacity borders blend with their background. A low-opacity white border on a dark surface is barely there — it defines the edge without demanding attention. Solid hex borders look harsh in comparison.
|
||||||
|
|
||||||
|
**The test:** Look at your interface from arm's length. If borders are the first thing you notice, reduce opacity. If you can't find where regions end, increase slightly.
|
||||||
|
|
||||||
|
### The Sidebar Decision
|
||||||
|
|
||||||
|
**Why same background as canvas, not different?**
|
||||||
|
|
||||||
|
Many dashboards make the sidebar a different color. This fragments the visual space — now you have "sidebar world" and "content world."
|
||||||
|
|
||||||
|
Better: Same background, subtle border separation. The sidebar is part of the app, not a separate region. Vercel does this. Supabase does this. The border is enough.
|
||||||
|
|
||||||
|
### The Dropdown Decision
|
||||||
|
|
||||||
|
**Why surface-200, not surface-100?**
|
||||||
|
|
||||||
|
The dropdown floats above the card it emerged from. If both were surface-100, the dropdown would blend into the card — you'd lose the sense of layering. Surface-200 is just light enough to feel "above" without being dramatically different.
|
||||||
|
|
||||||
|
**Why border-overlay instead of border-default?**
|
||||||
|
|
||||||
|
Overlays (dropdowns, popovers) often need slightly more definition because they're floating in space. A touch more border opacity helps them feel contained without being harsh.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example: Form Controls
|
||||||
|
|
||||||
|
### Input Background Decision
|
||||||
|
|
||||||
|
**Why darker, not lighter?**
|
||||||
|
|
||||||
|
Inputs are "inset" — they receive content, they don't project it. A slightly darker background signals "type here" without needing heavy borders. This is the alternative-background principle.
|
||||||
|
|
||||||
|
### Focus State Decision
|
||||||
|
|
||||||
|
**Why subtle focus states?**
|
||||||
|
|
||||||
|
Focus needs to be visible, but you don't need a glowing ring or dramatic color. A noticeable increase in border opacity is enough for a clear state change. Subtle-but-noticeable — the same principle as surfaces.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Adapt to Context
|
||||||
|
|
||||||
|
Your product might need:
|
||||||
|
- Warmer hues (slight yellow/orange tint)
|
||||||
|
- Cooler hues (blue-gray base)
|
||||||
|
- Different lightness progression
|
||||||
|
- Light mode (principles invert — higher elevation = shadow, not lightness)
|
||||||
|
|
||||||
|
**The principle is constant:** barely different, still distinguishable. The values adapt to context.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The Craft Check
|
||||||
|
|
||||||
|
Apply the squint test to your work:
|
||||||
|
|
||||||
|
1. Blur your eyes or step back
|
||||||
|
2. Can you still perceive hierarchy?
|
||||||
|
3. Is anything jumping out at you?
|
||||||
|
4. Can you tell where regions begin and end?
|
||||||
|
|
||||||
|
If hierarchy is visible and nothing is harsh — the subtle layering is working.
|
||||||
235
.agents/skills/interface-design/references/principles.md
Normal file
235
.agents/skills/interface-design/references/principles.md
Normal file
@@ -0,0 +1,235 @@
|
|||||||
|
# Core Craft Principles
|
||||||
|
|
||||||
|
These apply regardless of design direction. This is the quality floor.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Surface & Token Architecture
|
||||||
|
|
||||||
|
Professional interfaces don't pick colors randomly — they build systems. Understanding this architecture is the difference between "looks okay" and "feels like a real product."
|
||||||
|
|
||||||
|
### The Primitive Foundation
|
||||||
|
|
||||||
|
Every color in your interface should trace back to a small set of primitives:
|
||||||
|
|
||||||
|
- **Foreground** — text colors (primary, secondary, muted)
|
||||||
|
- **Background** — surface colors (base, elevated, overlay)
|
||||||
|
- **Border** — edge colors (default, subtle, strong)
|
||||||
|
- **Brand** — your primary accent
|
||||||
|
- **Semantic** — functional colors (destructive, warning, success)
|
||||||
|
|
||||||
|
Don't invent new colors. Map everything to these primitives.
|
||||||
|
|
||||||
|
### Surface Elevation Hierarchy
|
||||||
|
|
||||||
|
Surfaces stack. A dropdown sits above a card which sits above the page. Build a numbered system:
|
||||||
|
|
||||||
|
```
|
||||||
|
Level 0: Base background (the app canvas)
|
||||||
|
Level 1: Cards, panels (same visual plane as base)
|
||||||
|
Level 2: Dropdowns, popovers (floating above)
|
||||||
|
Level 3: Nested dropdowns, stacked overlays
|
||||||
|
Level 4: Highest elevation (rare)
|
||||||
|
```
|
||||||
|
|
||||||
|
In dark mode, higher elevation = slightly lighter. In light mode, higher elevation = slightly lighter or uses shadow. The principle: **elevated surfaces need visual distinction from what's beneath them.**
|
||||||
|
|
||||||
|
### The Subtlety Principle
|
||||||
|
|
||||||
|
This is where most interfaces fail. Study Vercel, Supabase, Linear — their surfaces are **barely different** but still distinguishable. Their borders are **light but not invisible**.
|
||||||
|
|
||||||
|
**For surfaces:** The difference between elevation levels should be subtle — a few percentage points of lightness, not dramatic jumps. In dark mode, surface-100 might be 7% lighter than base, surface-200 might be 9%, surface-300 might be 12%. You can barely see it, but you feel it.
|
||||||
|
|
||||||
|
**For borders:** Borders should define regions without demanding attention. Use low opacity (0.05-0.12 alpha for dark mode, slightly higher for light). The border should disappear when you're not looking for it, but be findable when you need to understand the structure.
|
||||||
|
|
||||||
|
**The test:** Squint at your interface. You should still perceive the hierarchy — what's above what, where regions begin and end. But no single border or surface should jump out at you. If borders are the first thing you notice, they're too strong. If you can't find where one region ends and another begins, they're too subtle.
|
||||||
|
|
||||||
|
**Common AI mistakes to avoid:**
|
||||||
|
- Borders that are too visible (1px solid gray instead of subtle rgba)
|
||||||
|
- Surface jumps that are too dramatic (going from dark to light instead of dark to slightly-less-dark)
|
||||||
|
- Using different hues for different surfaces (gray card on blue background)
|
||||||
|
- Harsh dividers where subtle borders would do
|
||||||
|
|
||||||
|
### Text Hierarchy via Tokens
|
||||||
|
|
||||||
|
Don't just have "text" and "gray text." Build four levels:
|
||||||
|
|
||||||
|
- **Primary** — default text, highest contrast
|
||||||
|
- **Secondary** — supporting text, slightly muted
|
||||||
|
- **Tertiary** — metadata, timestamps, less important
|
||||||
|
- **Muted** — disabled, placeholder, lowest contrast
|
||||||
|
|
||||||
|
Use all four consistently. If you're only using two, your hierarchy is too flat.
|
||||||
|
|
||||||
|
### Border Progression
|
||||||
|
|
||||||
|
Borders aren't binary. Build a scale:
|
||||||
|
|
||||||
|
- **Default** — standard borders
|
||||||
|
- **Subtle/Muted** — softer separation
|
||||||
|
- **Strong** — emphasis, hover states
|
||||||
|
- **Stronger** — maximum emphasis, focus rings
|
||||||
|
|
||||||
|
Match border intensity to the importance of the boundary.
|
||||||
|
|
||||||
|
### Dedicated Control Tokens
|
||||||
|
|
||||||
|
Form controls (inputs, checkboxes, selects) have specific needs. Don't just reuse surface tokens — create dedicated ones:
|
||||||
|
|
||||||
|
- **Control background** — often different from surface backgrounds
|
||||||
|
- **Control border** — needs to feel interactive
|
||||||
|
- **Control focus** — clear focus indication
|
||||||
|
|
||||||
|
This separation lets you tune controls independently from layout surfaces.
|
||||||
|
|
||||||
|
### Context-Aware Bases
|
||||||
|
|
||||||
|
Different areas of your app might need different base surfaces:
|
||||||
|
|
||||||
|
- **Marketing pages** — might use darker/richer backgrounds
|
||||||
|
- **Dashboard/app** — might use neutral working backgrounds
|
||||||
|
- **Sidebar** — might differ from main canvas
|
||||||
|
|
||||||
|
The surface hierarchy works the same way — it just starts from a different base.
|
||||||
|
|
||||||
|
### Alternative Backgrounds for Depth
|
||||||
|
|
||||||
|
Beyond shadows, use contrasting backgrounds to create depth. An "alternative" or "inset" background makes content feel recessed. Useful for:
|
||||||
|
|
||||||
|
- Empty states in data grids
|
||||||
|
- Code blocks
|
||||||
|
- Inset panels
|
||||||
|
- Visual grouping without borders
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Spacing System
|
||||||
|
|
||||||
|
Pick a base unit (4px and 8px are common) and use multiples throughout. The specific number matters less than consistency — every spacing value should be explainable as "X times the base unit."
|
||||||
|
|
||||||
|
Build a scale for different contexts:
|
||||||
|
- Micro spacing (icon gaps, tight element pairs)
|
||||||
|
- Component spacing (within buttons, inputs, cards)
|
||||||
|
- Section spacing (between related groups)
|
||||||
|
- Major separation (between distinct sections)
|
||||||
|
|
||||||
|
## Symmetrical Padding
|
||||||
|
|
||||||
|
TLBR must match. If top padding is 16px, left/bottom/right must also be 16px. Exception: when content naturally creates visual balance.
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Good */
|
||||||
|
padding: 16px;
|
||||||
|
padding: 12px 16px; /* Only when horizontal needs more room */
|
||||||
|
|
||||||
|
/* Bad */
|
||||||
|
padding: 24px 16px 12px 16px;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Border Radius Consistency
|
||||||
|
|
||||||
|
Sharper corners feel technical, rounder corners feel friendly. Pick a scale that fits your product's personality and use it consistently.
|
||||||
|
|
||||||
|
The key is having a system: small radius for inputs and buttons, medium for cards, large for modals or containers. Don't mix sharp and soft randomly — inconsistent radius is as jarring as inconsistent spacing.
|
||||||
|
|
||||||
|
## Depth & Elevation Strategy
|
||||||
|
|
||||||
|
Match your depth approach to your design direction. Choose ONE and commit:
|
||||||
|
|
||||||
|
**Borders-only (flat)** — Clean, technical, dense. Works for utility-focused tools where information density matters more than visual lift. Linear, Raycast, and many developer tools use almost no shadows — just subtle borders to define regions.
|
||||||
|
|
||||||
|
**Subtle single shadows** — Soft lift without complexity. A simple `0 1px 3px rgba(0,0,0,0.08)` can be enough. Works for approachable products that want gentle depth.
|
||||||
|
|
||||||
|
**Layered shadows** — Rich, premium, dimensional. Multiple shadow layers create realistic depth. Stripe and Mercury use this approach. Best for cards that need to feel like physical objects.
|
||||||
|
|
||||||
|
**Surface color shifts** — Background tints establish hierarchy without any shadows. A card at `#fff` on a `#f8fafc` background already feels elevated.
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Borders-only approach */
|
||||||
|
--border: rgba(0, 0, 0, 0.08);
|
||||||
|
--border-subtle: rgba(0, 0, 0, 0.05);
|
||||||
|
border: 0.5px solid var(--border);
|
||||||
|
|
||||||
|
/* Single shadow approach */
|
||||||
|
--shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
/* Layered shadow approach */
|
||||||
|
--shadow-layered:
|
||||||
|
0 0 0 0.5px rgba(0, 0, 0, 0.05),
|
||||||
|
0 1px 2px rgba(0, 0, 0, 0.04),
|
||||||
|
0 2px 4px rgba(0, 0, 0, 0.03),
|
||||||
|
0 4px 8px rgba(0, 0, 0, 0.02);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Card Layouts
|
||||||
|
|
||||||
|
Monotonous card layouts are lazy design. A metric card doesn't have to look like a plan card doesn't have to look like a settings card.
|
||||||
|
|
||||||
|
Design each card's internal structure for its specific content — but keep the surface treatment consistent: same border weight, shadow depth, corner radius, padding scale, typography.
|
||||||
|
|
||||||
|
## Isolated Controls
|
||||||
|
|
||||||
|
UI controls deserve container treatment. Date pickers, filters, dropdowns — these should feel like crafted objects.
|
||||||
|
|
||||||
|
**Never use native form elements for styled UI.** Native `<select>`, `<input type="date">`, and similar elements render OS-native dropdowns that cannot be styled. Build custom components instead:
|
||||||
|
|
||||||
|
- Custom select: trigger button + positioned dropdown menu
|
||||||
|
- Custom date picker: input + calendar popover
|
||||||
|
- Custom checkbox/radio: styled div with state management
|
||||||
|
|
||||||
|
Custom select triggers must use `display: inline-flex` with `white-space: nowrap` to keep text and chevron icons on the same row.
|
||||||
|
|
||||||
|
## Typography Hierarchy
|
||||||
|
|
||||||
|
Build distinct levels that are visually distinguishable at a glance:
|
||||||
|
|
||||||
|
- **Headlines** — heavier weight, tighter letter-spacing for presence
|
||||||
|
- **Body** — comfortable weight for readability
|
||||||
|
- **Labels/UI** — medium weight, works at smaller sizes
|
||||||
|
- **Data** — often monospace, needs `tabular-nums` for alignment
|
||||||
|
|
||||||
|
Don't rely on size alone. Combine size, weight, and letter-spacing to create clear hierarchy. If you squint and can't tell headline from body, the hierarchy is too weak.
|
||||||
|
|
||||||
|
## Monospace for Data
|
||||||
|
|
||||||
|
Numbers, IDs, codes, timestamps belong in monospace. Use `tabular-nums` for columnar alignment. Mono signals "this is data."
|
||||||
|
|
||||||
|
## Iconography
|
||||||
|
|
||||||
|
Icons clarify, not decorate — if removing an icon loses no meaning, remove it. Choose a consistent icon set and stick with it throughout the product.
|
||||||
|
|
||||||
|
Give standalone icons presence with subtle background containers. Icons next to text should align optically, not mathematically.
|
||||||
|
|
||||||
|
## Animation
|
||||||
|
|
||||||
|
Keep it fast and functional. Micro-interactions (hover, focus) should feel instant — around 150ms. Larger transitions (modals, panels) can be slightly longer — 200-250ms.
|
||||||
|
|
||||||
|
Use smooth deceleration easing (ease-out variants). Avoid spring/bounce effects in professional interfaces — they feel playful, not serious.
|
||||||
|
|
||||||
|
## Contrast Hierarchy
|
||||||
|
|
||||||
|
Build a four-level system: foreground (primary) → secondary → muted → faint. Use all four consistently.
|
||||||
|
|
||||||
|
## Color Carries Meaning
|
||||||
|
|
||||||
|
Gray builds structure. Color communicates — status, action, emphasis, identity. Unmotivated color is noise. Color that reinforces the product's world is character.
|
||||||
|
|
||||||
|
## Navigation Context
|
||||||
|
|
||||||
|
Screens need grounding. A data table floating in space feels like a component demo, not a product. Consider including:
|
||||||
|
|
||||||
|
- **Navigation** — sidebar or top nav showing where you are in the app
|
||||||
|
- **Location indicator** — breadcrumbs, page title, or active nav state
|
||||||
|
- **User context** — who's logged in, what workspace/org
|
||||||
|
|
||||||
|
When building sidebars, consider using the same background as the main content area. Rely on a subtle border for separation rather than different background colors.
|
||||||
|
|
||||||
|
## Dark Mode
|
||||||
|
|
||||||
|
Dark interfaces have different needs:
|
||||||
|
|
||||||
|
**Borders over shadows** — Shadows are less visible on dark backgrounds. Lean more on borders for definition.
|
||||||
|
|
||||||
|
**Adjust semantic colors** — Status colors (success, warning, error) often need to be slightly desaturated for dark backgrounds.
|
||||||
|
|
||||||
|
**Same structure, different values** — The hierarchy system still applies, just with inverted values.
|
||||||
48
.agents/skills/interface-design/references/validation.md
Normal file
48
.agents/skills/interface-design/references/validation.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Memory Management
|
||||||
|
|
||||||
|
When and how to update `.interface-design/system.md`.
|
||||||
|
|
||||||
|
## When to Add Patterns
|
||||||
|
|
||||||
|
Add to system.md when:
|
||||||
|
- Component used 2+ times
|
||||||
|
- Pattern is reusable across the project
|
||||||
|
- Has specific measurements worth remembering
|
||||||
|
|
||||||
|
## Pattern Format
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
### Button Primary
|
||||||
|
- Height: 36px
|
||||||
|
- Padding: 12px 16px
|
||||||
|
- Radius: 6px
|
||||||
|
- Font: 14px, 500 weight
|
||||||
|
```
|
||||||
|
|
||||||
|
## Don't Document
|
||||||
|
|
||||||
|
- One-off components
|
||||||
|
- Temporary experiments
|
||||||
|
- Variations better handled with props
|
||||||
|
|
||||||
|
## Pattern Reuse
|
||||||
|
|
||||||
|
Before creating a component, check system.md:
|
||||||
|
- Pattern exists? Use it.
|
||||||
|
- Need variation? Extend, don't create new.
|
||||||
|
|
||||||
|
Memory compounds: each pattern saved makes future work faster and more consistent.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Validation Checks
|
||||||
|
|
||||||
|
If system.md defines specific values, check consistency:
|
||||||
|
|
||||||
|
**Spacing** — All values multiples of the defined base?
|
||||||
|
|
||||||
|
**Depth** — Using the declared strategy throughout? (borders-only means no shadows)
|
||||||
|
|
||||||
|
**Colors** — Using defined palette, not random hex codes?
|
||||||
|
|
||||||
|
**Patterns** — Reusing documented patterns instead of creating new?
|
||||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -1,5 +1,4 @@
|
|||||||
node_modules
|
node_modules
|
||||||
bun.lock
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
# ---> Node
|
# ---> Node
|
||||||
# Logs
|
# Logs
|
||||||
@@ -10,6 +9,7 @@ yarn-debug.log*
|
|||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
.pnpm-debug.log*
|
.pnpm-debug.log*
|
||||||
|
.vite
|
||||||
|
|
||||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
@@ -78,6 +78,7 @@ web_modules/
|
|||||||
|
|
||||||
# dotenv environment variable files
|
# dotenv environment variable files
|
||||||
.env
|
.env
|
||||||
|
apps/backend/.env
|
||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
@@ -139,3 +140,19 @@ dist
|
|||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
# Bun
|
||||||
|
bun.lock
|
||||||
|
|
||||||
|
# Generated
|
||||||
|
apps/backend/src/generated
|
||||||
|
apps/backend/prisma.config.js
|
||||||
|
apps/backend/prisma/*.js
|
||||||
|
apps/backend/prisma/*.seed-data.js
|
||||||
|
apps/backend/src/app.js
|
||||||
|
apps/backend/src/lib/*.js
|
||||||
|
apps/backend/src/middlewares/*.js
|
||||||
|
apps/backend/src/modules/**/*.js
|
||||||
|
apps/backend/src/register-routes.js
|
||||||
|
apps/backend/src/server.js
|
||||||
|
apps/backend/src/index.js
|
||||||
|
apps/backend/src/types/*.js
|
||||||
|
|||||||
243
AGENTS.md
Normal file
243
AGENTS.md
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Root (runs both frontend + backend)
|
||||||
|
bun run dev
|
||||||
|
|
||||||
|
# Individual packages
|
||||||
|
bun run dev:frontend
|
||||||
|
bun run dev:backend
|
||||||
|
bun run build:frontend
|
||||||
|
|
||||||
|
# Linting (Biome - ESLint was replaced)
|
||||||
|
bun run lint
|
||||||
|
bun run lint:fix # fixes and formats
|
||||||
|
|
||||||
|
# Prisma (run from backend)
|
||||||
|
bun --filter backend prisma:generate # regenerate client after schema changes
|
||||||
|
bun --filter backend prisma:migrate # apply migrations
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/frontend/ # React + Vite + TanStack Router/Query
|
||||||
|
apps/backend/ # Bun + Hono + Prisma
|
||||||
|
packages/api-contract/ # Shared Zod schemas, types, route definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
**Shared contract pattern:**
|
||||||
|
1. Update `packages/api-contract` first (schemas/types)
|
||||||
|
2. Implement handler in `apps/backend`
|
||||||
|
3. Consume from `apps/frontend` via workspace import `@repo/api-contract`
|
||||||
|
|
||||||
|
## Authentication (Better Auth)
|
||||||
|
|
||||||
|
The project uses **Better Auth** for session management, replacing the legacy Supabase Auth.
|
||||||
|
|
||||||
|
- **Backend Context**: `AppContext` (mapped via `AppEnv`) provides `c.get('user')` and `c.get('session')`.
|
||||||
|
- **User Roles**: The `User` model includes a `role` field (default: `member`). Use `requireSuperAdmin` middleware for protected admin routes.
|
||||||
|
- **Session Persistence**:
|
||||||
|
- Backend: Hono CORS must have `credentials: true`.
|
||||||
|
- Frontend: `authClient` must have `fetchOptions.credentials: 'include'`.
|
||||||
|
- Frontend: Axios instance must have `withCredentials: true`.
|
||||||
|
|
||||||
|
## Key Quirks
|
||||||
|
|
||||||
|
- **Prisma 7**: Uses `prisma.config.ts`, requires `DATABASE_URL` env var at generate time.
|
||||||
|
- **Gravatar Fallback**: Users without an `image` get an automatic Gravatar Identicon.
|
||||||
|
- Backend: Handled in `user.service.ts` for profile API.
|
||||||
|
- Frontend: Handled in `AuthProvider` (`auth.tsx`) for the session state.
|
||||||
|
- **Zod v4**: Use `.issues` instead of `.errors` for validation errors.
|
||||||
|
- **TanStack Router**: Routes are code-generated. Use `--filter frontend build` not raw vite build.
|
||||||
|
|
||||||
|
## Real-time Updates (SSE)
|
||||||
|
|
||||||
|
The project uses **Server-Sent Events (SSE)** for real-time booking updates in the admin panel.
|
||||||
|
|
||||||
|
- **Backend**: `apps/backend/src/lib/sse.ts` provides the `sseManager` and SSE endpoint `/api/events/:channel`.
|
||||||
|
- **Event flow**: When a public booking is created, the handler emits to channel `complex:{complexId}`.
|
||||||
|
- **Frontend**: `home-page.tsx` connects to the SSE endpoint and invalidates the bookings query on new events.
|
||||||
|
|
||||||
|
**Important - Multi-process limitation**: The current SSE implementation works with a single Bun process only. If you deploy with multiple workers (e.g., clustering in Dokploy), events won't be shared across workers. For production with multiple workers, use Redis for pub/sub:
|
||||||
|
- Install `@hono/node-server` or a Redis client
|
||||||
|
- Replace `sseManager` with Redis pub/sub
|
||||||
|
- Or fall back to Socket.io with Redis adapter
|
||||||
|
|
||||||
|
## Docker Deployment (Dokploy)
|
||||||
|
|
||||||
|
- **Build Context**: `./` (monorepo root)
|
||||||
|
- **Dockerfile**: `Dockerfile` (in root, not `apps/backend/`)
|
||||||
|
- **Required args**: `DATABASE_URL`, `VITE_API_BASE_URL`, `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL`
|
||||||
|
- **bun.lock must be tracked**: It's in `.gitignore` by default - remove it for Docker builds
|
||||||
|
|
||||||
|
## Linting
|
||||||
|
|
||||||
|
Biome is used (not ESLint). Config in `biome.json`:
|
||||||
|
- Line width: 100
|
||||||
|
- Quote style: single
|
||||||
|
- Semicolons: always
|
||||||
|
- `noUnusedVariables`: warn
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
**Backend** (`apps/backend/.env`):
|
||||||
|
- `DATABASE_URL` - PostgreSQL connection (required for Prisma)
|
||||||
|
- `BETTER_AUTH_SECRET`, `BETTER_AUTH_URL` - Auth core
|
||||||
|
- `APP_BASE_URL` - Frontend URL for trusted origins
|
||||||
|
- `CORS_ORIGIN` - Frontend URL for CORS policy
|
||||||
|
- `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` - Google OAuth (see below)
|
||||||
|
|
||||||
|
**Frontend** (`apps/frontend/.env`):
|
||||||
|
- `VITE_*` prefix required (Vite embeds these at build time)
|
||||||
|
- `VITE_API_BASE_URL` - Backend URL (default: http://localhost:3000)
|
||||||
|
|
||||||
|
## Google OAuth
|
||||||
|
|
||||||
|
To enable login with Google:
|
||||||
|
|
||||||
|
1. Create OAuth credentials in Google Cloud Console:
|
||||||
|
- Application type: Web application
|
||||||
|
- Authorized redirect URI: `{BETTER_AUTH_URL}/api/auth/callback/google`
|
||||||
|
|
||||||
|
2. Add env vars to `.env.example`:
|
||||||
|
```
|
||||||
|
GOOGLE_CLIENT_ID=your-google-client-id
|
||||||
|
GOOGLE_CLIENT_SECRET=your-google-client-secret
|
||||||
|
```
|
||||||
|
|
||||||
|
3. The backend configures `socialProviders.google` automatically.
|
||||||
|
|
||||||
|
4. Frontend uses `authClient.signIn.social({ provider: 'google', callbackURL: 'http://localhost:5173' })`.
|
||||||
|
|
||||||
|
## Complex Selection (Multi-tenancy)
|
||||||
|
|
||||||
|
The app supports users with multiple complexes. Each user has a role per complex (`ADMIN` or `EMPLOYEE`), stored in `ComplexUser` table.
|
||||||
|
|
||||||
|
### Flow
|
||||||
|
|
||||||
|
1. **Login** (password or Google OAuth):
|
||||||
|
- After auth, call `GET /api/complexes/mine` to get user's complexes
|
||||||
|
- If 1 complex → auto-select via `POST /api/complexes/select` → redirect to `/`
|
||||||
|
- If >1 complexes → redirect to `/select-complex`
|
||||||
|
|
||||||
|
2. **Select Complex Page** (`/select-complex`):
|
||||||
|
- List all complexes with roles
|
||||||
|
- User selects one → `POST /api/complexes/select` → set cookie → redirect to `/`
|
||||||
|
|
||||||
|
3. **Home Page** (`/`):
|
||||||
|
- If no cookie (first visit) → auto-select if 1 complex, else redirect
|
||||||
|
- Use `GET /api/complexes/me` to get current selected complex
|
||||||
|
|
||||||
|
### Cookie
|
||||||
|
|
||||||
|
- Name: `selected-complex-id`
|
||||||
|
- httpOnly, secure (prod), sameSite: strict
|
||||||
|
- MaxAge: 30 days
|
||||||
|
- Set by `POST /api/complexes/select`
|
||||||
|
|
||||||
|
### Endpoints
|
||||||
|
|
||||||
|
| Method | Endpoint | Description |
|
||||||
|
|--------|----------|-------------|
|
||||||
|
| GET | `/api/complexes/mine` | List all complexes with role |
|
||||||
|
| GET | `/api/complexes/me` | Get currently selected complex |
|
||||||
|
| POST | `/api/complexes/select` | Select complex (sets cookie) |
|
||||||
|
|
||||||
|
### Profile Role
|
||||||
|
|
||||||
|
The profile page shows the user's role for the **selected complex**, not the global user role. This is fetched from `ComplexUser` table using the cookie value.
|
||||||
|
|
||||||
|
## Frontend API Client
|
||||||
|
|
||||||
|
The API client is split into modular files for better maintainability and testability.
|
||||||
|
|
||||||
|
### Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
lib/
|
||||||
|
├── api-client.ts # Main barrel file (~70 lines)
|
||||||
|
└── api/
|
||||||
|
├── base.ts # ApiClientError, configureApiClient, apiBaseUrl
|
||||||
|
├── http.ts # Axios instance with interceptors
|
||||||
|
├── index.ts # Re-exports all resources
|
||||||
|
└── resources/
|
||||||
|
├── complexes.ts # Complex endpoints
|
||||||
|
├── courts.ts # Court endpoints
|
||||||
|
├── bookings.ts # Public & admin booking endpoints
|
||||||
|
├── user.ts # User profile endpoint
|
||||||
|
├── sports.ts # Sport endpoints
|
||||||
|
├── onboarding.ts # Onboarding endpoints
|
||||||
|
└── plans.ts # Plan endpoints
|
||||||
|
```
|
||||||
|
|
||||||
|
### Using the API Client
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { apiClient, authClient, ApiClientError, configureApiClient } from '@/lib/api-client';
|
||||||
|
|
||||||
|
// Via apiClient (barrel)
|
||||||
|
await apiClient.complexes.listMine();
|
||||||
|
await apiClient.publicBookings.getAvailability('my-club', { date: '2026-04-20' });
|
||||||
|
|
||||||
|
// Direct imports (for better tree-shaking)
|
||||||
|
import { complexes, getAvailability } from '@/lib/api';
|
||||||
|
await complexes.listMine();
|
||||||
|
await getAvailability('my-club', { date: '2026-04-20' });
|
||||||
|
```
|
||||||
|
|
||||||
|
### Adding a New Resource
|
||||||
|
|
||||||
|
1. Create `lib/api/resources/[resource].ts`
|
||||||
|
2. Export functions using `http` from `../http`
|
||||||
|
3. Add exports in `lib/api/index.ts`
|
||||||
|
|
||||||
|
## Email Templates
|
||||||
|
|
||||||
|
Todos los emails deben usar la misma estética. El layout compartido está en `apps/backend/src/emails/booking-confirmation.ts`.
|
||||||
|
|
||||||
|
### Layout (`wrapLayout`)
|
||||||
|
|
||||||
|
Exportado como `wrapLayout(content)`. Proporciona:
|
||||||
|
- Fondo: `#edf7f4`
|
||||||
|
- Card blanca: `max-width: 520px`, `border-radius: 28px`, `box-shadow: 0 24px 70px rgba(15,23,42,0.12)`, `border: 1px solid rgba(5,9,20,0.1)`
|
||||||
|
- Playzer favicon: `{APP_BASE_URL}/playzer-favicon-512-transparent.png` (está en `apps/frontend/public/`)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { wrapLayout } from '@/emails/booking-confirmation';
|
||||||
|
|
||||||
|
const html = wrapLayout(`<tr>...contenido...</tr>`);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Estructura de cada email
|
||||||
|
|
||||||
|
| Sección | Descripción |
|
||||||
|
|---------|-------------|
|
||||||
|
| **Header** | Dos columnas: badge pill a la izquierda + Playzer (favicon + texto) a la derecha. El badge usa `border-radius: 999px`, `padding: 4px 12px`, `font-size: 11px`, `font-weight: 700`, `letter-spacing: 0.14em`, `text-transform: uppercase`. |
|
||||||
|
| **Card fecha/hora** | Fondo `#f0fdf4`, borde `1px solid rgba(5,150,105,0.3)`, `border-radius: 24px`. Siempre verde aunque el email sea de cancelación. |
|
||||||
|
| **Grilla detalles** | `border: 1px solid #e5e7eb`, `border-radius: 22px`, dos celdas de 50% con `border-right` en la primera. |
|
||||||
|
| **Botón CTA** | Tabla con fondo `#059669`, `border-radius: 12px`, link blanco con `padding: 14px 32px`. |
|
||||||
|
| **Pie** | Sin pie de marca. Solo texto secundario opcional centrado si es necesario. |
|
||||||
|
| **Sin botones de acción** | Los emails de booking **no** incluyen los botones "Compartir por WhatsApp" ni "Hacer otra reserva". |
|
||||||
|
|
||||||
|
### Colores de badges según estado
|
||||||
|
|
||||||
|
| Estado | Fondo badge | Texto badge |
|
||||||
|
|--------|-------------|-------------|
|
||||||
|
| Confirmado | `#f0fdf4` | `#15803d` |
|
||||||
|
| Cancelado | `#fef2f2` | `#dc2626` |
|
||||||
|
| No concretado | `#fffbeb` | `#d97706` |
|
||||||
|
| Neutro (verificación, etc.) | `#f4f4f5` | `#71717a` |
|
||||||
|
|
||||||
|
### Archivos de templates
|
||||||
|
|
||||||
|
| Archivo | Templates |
|
||||||
|
|---------|-----------|
|
||||||
|
| `apps/backend/src/emails/booking-confirmation.ts` | `bookingConfirmationHtml`, `bookingCancelledHtml`, `bookingNoShowHtml` + exporta `wrapLayout` |
|
||||||
|
| `apps/backend/src/lib/auth.ts` | Email de verificación de Better Auth (usa `wrapLayout` inline) |
|
||||||
|
|
||||||
|
### Regla general
|
||||||
|
|
||||||
|
Para emails nuevos: importar `wrapLayout`, construir el HTML interno con `<tr>`s, usar la misma estructura de cabecera (badge + Playzer), y nunca incluir el pie "Playzer — Reserva de canchas online". Usar `APP_BASE_URL` para construir URLs absolutas al logo.
|
||||||
63
Dockerfile
Normal file
63
Dockerfile
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
# syntax=docker/dockerfile:1.7
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 1: Install frontend dependencies
|
||||||
|
# =============================================================================
|
||||||
|
FROM oven/bun:1.3.11 AS deps-frontend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
COPY apps/frontend/package.json apps/frontend/
|
||||||
|
COPY packages/api-contract/package.json packages/api-contract/
|
||||||
|
RUN bun install
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 2: Build Frontend
|
||||||
|
# =============================================================================
|
||||||
|
FROM deps-frontend AS build-frontend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY apps/frontend ./apps/frontend
|
||||||
|
COPY packages ./packages
|
||||||
|
ARG VITE_API_BASE_URL
|
||||||
|
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
|
||||||
|
RUN bun run build:frontend
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 3: Install backend dependencies
|
||||||
|
# =============================================================================
|
||||||
|
FROM oven/bun:1.3.11 AS deps-backend
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
COPY apps/backend/package.json apps/backend/
|
||||||
|
COPY packages/api-contract/package.json packages/api-contract/
|
||||||
|
RUN bun install
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 4: Build Backend (prisma generate + tsc)
|
||||||
|
# =============================================================================
|
||||||
|
FROM deps-backend AS build-backend
|
||||||
|
WORKDIR /app
|
||||||
|
ARG DATABASE_URL
|
||||||
|
ENV DATABASE_URL=${DATABASE_URL:-postgresql://dummy:dummy@localhost:5432/dummy}
|
||||||
|
COPY apps/backend ./apps/backend
|
||||||
|
COPY packages ./packages
|
||||||
|
RUN bun --cwd apps/backend prisma:generate
|
||||||
|
RUN bun --cwd apps/backend build
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Stage 5: Runner - Production image
|
||||||
|
# =============================================================================
|
||||||
|
FROM oven/bun:1.3.11 AS runner
|
||||||
|
WORKDIR /app
|
||||||
|
ENV NODE_ENV=production HOST=0.0.0.0 PORT=3000
|
||||||
|
|
||||||
|
COPY --from=build-backend /app/node_modules ./node_modules
|
||||||
|
COPY --from=build-frontend /app/apps/frontend/dist ./apps/backend/public
|
||||||
|
COPY --from=build-backend /app/apps/backend ./apps/backend
|
||||||
|
COPY --from=build-backend /app/packages ./packages
|
||||||
|
COPY package.json bun.lock ./
|
||||||
|
COPY entrypoint.sh /app/entrypoint.sh
|
||||||
|
|
||||||
|
RUN chmod +x /app/entrypoint.sh
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["/app/entrypoint.sh"]
|
||||||
183
README.md
183
README.md
@@ -1,184 +1,105 @@
|
|||||||
# Monorepo (Frontend + Backend + Contrato API)
|
# Playzer Monorepo
|
||||||
|
|
||||||
Este repositorio contiene:
|
Este repositorio contiene:
|
||||||
|
|
||||||
- `apps/frontend`: app web en React + Vite + TanStack Router/Query.
|
- `apps/frontend`: React + Vite + TanStack Router/Query.
|
||||||
- `apps/backend`: API en Bun + Hono + Prisma + Supabase Auth.
|
- `apps/backend`: Bun + Hono + Prisma + Better Auth.
|
||||||
- `packages/api-contract`: contratos compartidos (schemas/types/rutas) entre frontend y backend.
|
- `packages/api-contract`: contratos Zod/tipos compartidos.
|
||||||
|
|
||||||
## Objetivo de este README
|
## Requisitos
|
||||||
|
|
||||||
Dejarte productivo/a en pocos minutos para que puedas:
|
- Bun reciente
|
||||||
|
- PostgreSQL disponible
|
||||||
1. Levantar frontend y backend en local.
|
|
||||||
2. Configurar variables de entorno.
|
|
||||||
3. Ejecutar migraciones de base de datos.
|
|
||||||
4. Entender la estructura y el flujo de trabajo del proyecto.
|
|
||||||
|
|
||||||
## Stack técnico
|
|
||||||
|
|
||||||
- Runtime/package manager: `bun`
|
|
||||||
- Frontend: `React 19`, `Vite`, `TanStack Router`, `TanStack Query`
|
|
||||||
- Backend: `Bun`, `Hono`, `Prisma`, `PostgreSQL`
|
|
||||||
- Auth: `Supabase Auth` (token Bearer validado en backend)
|
|
||||||
- Validación de contratos: `zod` en `@repo/api-contract`
|
|
||||||
|
|
||||||
## Prerrequisitos
|
|
||||||
|
|
||||||
- `bun` instalado (versión reciente).
|
|
||||||
- `PostgreSQL` corriendo localmente (o accesible remotamente).
|
|
||||||
- Proyecto de `Supabase` para autenticación (URL + anon key).
|
|
||||||
|
|
||||||
## Setup rápido
|
## Setup rápido
|
||||||
|
|
||||||
1. Instalar dependencias desde la raíz:
|
1. Instalar dependencias:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun install
|
bun install
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Configurar variables de entorno del backend:
|
2. Configurar backend:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cp apps/backend/.env.example apps/backend/.env
|
cp apps/backend/.env.example apps/backend/.env
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Configurar variables de entorno del frontend:
|
3. Configurar frontend:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cp apps/frontend/.env.example apps/frontend/.env
|
cp apps/frontend/.env.example apps/frontend/.env
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Revisar/editar valores en ambos `.env` según tu entorno.
|
4. Ejecutar migraciones:
|
||||||
|
|
||||||
## Variables de entorno
|
|
||||||
|
|
||||||
### Backend (`apps/backend/.env`)
|
|
||||||
|
|
||||||
Variables esperadas:
|
|
||||||
|
|
||||||
- `SUPABASE_URL`
|
|
||||||
- `SUPABASE_ANON_KEY`
|
|
||||||
- `CORS_ORIGIN` (por defecto local frontend)
|
|
||||||
- `LOG_LEVEL`
|
|
||||||
- `DATABASE_URL` (PostgreSQL)
|
|
||||||
|
|
||||||
Notas:
|
|
||||||
|
|
||||||
- El backend también acepta `VITE_SUPABASE_URL` y `VITE_SUPABASE_ANON_KEY` como fallback.
|
|
||||||
- Si falta `DATABASE_URL`, Prisma no puede inicializarse.
|
|
||||||
|
|
||||||
### Frontend (`apps/frontend/.env`)
|
|
||||||
|
|
||||||
Variables esperadas:
|
|
||||||
|
|
||||||
- `VITE_SUPABASE_URL`
|
|
||||||
- `VITE_SUPABASE_ANON_KEY`
|
|
||||||
- `VITE_API_BASE_URL` (por defecto: `http://localhost:3000`)
|
|
||||||
|
|
||||||
## Base de datos (Prisma)
|
|
||||||
|
|
||||||
Antes de arrancar backend por primera vez, corré migraciones:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun --filter backend prisma:migrate
|
bun --filter backend prisma:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
Si cambiás el schema, regenerá cliente:
|
## Variables de entorno
|
||||||
|
|
||||||
```sh
|
### Backend (`apps/backend/.env`)
|
||||||
bun --filter backend prisma:generate
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ejecutar en desarrollo
|
- `DATABASE_URL`
|
||||||
|
- `BETTER_AUTH_SECRET`
|
||||||
|
- `BETTER_AUTH_URL`
|
||||||
|
- `APP_BASE_URL`
|
||||||
|
- `CORS_ORIGIN`
|
||||||
|
|
||||||
Terminal 1 (backend):
|
### Frontend (`apps/frontend/.env`)
|
||||||
|
|
||||||
|
- `VITE_API_BASE_URL`
|
||||||
|
|
||||||
|
## Desarrollo
|
||||||
|
|
||||||
|
Backend:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun run dev:backend
|
bun run dev:backend
|
||||||
```
|
```
|
||||||
|
|
||||||
Terminal 2 (frontend):
|
Frontend:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
bun run dev:frontend
|
bun run dev:frontend
|
||||||
```
|
```
|
||||||
|
|
||||||
Puertos por defecto:
|
Todo junto:
|
||||||
|
|
||||||
- Frontend: `http://localhost:5173`
|
```sh
|
||||||
- Backend: `http://localhost:3000`
|
bun run dev
|
||||||
|
|
||||||
## Scripts útiles
|
|
||||||
|
|
||||||
Desde la raíz:
|
|
||||||
|
|
||||||
- `bun run dev:frontend`
|
|
||||||
- `bun run dev:backend`
|
|
||||||
- `bun run build:frontend`
|
|
||||||
- `bun run lint:frontend`
|
|
||||||
|
|
||||||
Desde `apps/backend`:
|
|
||||||
|
|
||||||
- `bun run dev`
|
|
||||||
- `bun run start`
|
|
||||||
- `bun run prisma:migrate`
|
|
||||||
- `bun run prisma:generate`
|
|
||||||
|
|
||||||
## Estructura del repo
|
|
||||||
|
|
||||||
```txt
|
|
||||||
.
|
|
||||||
├─ apps/
|
|
||||||
│ ├─ frontend/ # UI y navegación
|
|
||||||
│ └─ backend/ # API, auth middleware, acceso a DB
|
|
||||||
└─ packages/
|
|
||||||
└─ api-contract/ # Schemas/tipos/rutas compartidos
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Flujo de autenticación (resumen)
|
## Prisma
|
||||||
|
|
||||||
1. Frontend autentica con Supabase.
|
Regenerar cliente después de cambios de schema:
|
||||||
2. Frontend guarda el access token y lo envía como `Authorization: Bearer ...` al backend.
|
|
||||||
3. Backend valida token con Supabase (`requireAuth` middleware).
|
|
||||||
4. Si el token es válido, habilita rutas protegidas (`/api/user/*`, `/api/tenants/*`).
|
|
||||||
|
|
||||||
## Endpoints principales
|
```sh
|
||||||
|
bun --filter backend prisma:generate
|
||||||
|
```
|
||||||
|
|
||||||
- `GET /api/user/profile`
|
## Autenticación
|
||||||
- `POST /api/tenants`
|
|
||||||
- `GET /api/tenants/:id`
|
|
||||||
- `PATCH /api/tenants/:id`
|
|
||||||
|
|
||||||
## Convenciones recomendadas para el equipo
|
El proyecto usa **Better Auth** con sesión por cookie.
|
||||||
|
|
||||||
- Mantener contratos de API en `packages/api-contract` (evitar duplicar tipos).
|
- Backend: `c.get('user')` y `c.get('session')` en contexto Hono.
|
||||||
- Si agregás/cambiás endpoint:
|
- Frontend: `authClient` con `fetchOptions.credentials = 'include'`.
|
||||||
1. Actualizá schema/tipos en `api-contract`.
|
- Frontend API: Axios con `withCredentials = true`.
|
||||||
2. Implementá handler/ruta en backend.
|
- Backend CORS: `credentials: true`.
|
||||||
3. Consumí el contrato desde frontend.
|
|
||||||
- Validar payloads con Zod tanto en contrato como en rutas.
|
|
||||||
- Mantener PRs chicos y enfocados.
|
|
||||||
|
|
||||||
## Troubleshooting rápido
|
## Deploy (Docker)
|
||||||
|
|
||||||
- Error de CORS:
|
- Build context: raíz del monorepo (`./`)
|
||||||
- Revisar `CORS_ORIGIN` en backend y que incluya `http://localhost:5173`.
|
- Dockerfile: `./Dockerfile`
|
||||||
- `403 Missing bearer token`:
|
- Build args requeridos:
|
||||||
- Verificar login en frontend y envío del header `Authorization`.
|
- `DATABASE_URL`
|
||||||
- Error de Supabase env vars:
|
- `VITE_API_BASE_URL`
|
||||||
- Confirmar `SUPABASE_URL`/`SUPABASE_ANON_KEY` (backend) y `VITE_*` (frontend).
|
- `BETTER_AUTH_SECRET`
|
||||||
- Error de Prisma/DB:
|
- `BETTER_AUTH_URL`
|
||||||
- Confirmar `DATABASE_URL`, conectividad a PostgreSQL y migraciones aplicadas.
|
|
||||||
|
|
||||||
## Primer día sugerido para onboarding
|
## Flujo recomendado de cambios de API
|
||||||
|
|
||||||
1. Levantar backend y frontend en local.
|
1. Actualizar contrato en `packages/api-contract`.
|
||||||
2. Crear usuario o iniciar sesión desde la pantalla de onboard.
|
2. Implementar backend en `apps/backend`.
|
||||||
3. Crear un tenant de prueba.
|
3. Consumir contrato desde frontend (`@repo/api-contract`).
|
||||||
4. Probar lectura/edición de tenant.
|
|
||||||
5. Revisar `packages/api-contract` para entender cómo se comparten tipos y rutas.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Si te trabás en el setup, empezá verificando `.env`, estado de PostgreSQL y que ambos procesos (`dev:backend` y `dev:frontend`) estén corriendo.
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
SUPABASE_URL=https://afzzqgvdkkpyxmifpaya.supabase.co
|
|
||||||
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFmenpxZ3Zka2tweXhtaWZwYXlhIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTc3NDg5Njc5MiwiZXhwIjoyMDkwNDcyNzkyfQ.2tex8lMCGzxBqeIk01Z08Wl15K7AavyfClUFgUl2NPM
|
|
||||||
SUPABASE_ANON_KEY=sb_publishable_RgMLVBTCTClhK-1Ks_X02Q_Sq1Z1Pq-
|
|
||||||
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
|
|
||||||
DATABASE_URL=postgres://super_admin:solotech25@localhost:5432/playzer-dev
|
|
||||||
APP_BASE_URL=http://localhost:5173
|
|
||||||
ONBOARDING_TTL_MINUTES=60
|
|
||||||
SMTP_HOST=sandbox.smtp.mailtrap.io
|
|
||||||
SMTP_PORT=587
|
|
||||||
SMTP_USER=6af2bf58132d12
|
|
||||||
SMTP_PASS=1253b2fe0fc47a
|
|
||||||
SMTP_FROM=Playzer <no-reply@playzer.app>
|
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
SUPABASE_URL=https://your-project-ref.supabase.co
|
BETTER_AUTH_SECRET=your-random-secret
|
||||||
SUPABASE_ANON_KEY=your-anon-key
|
BETTER_AUTH_URL=http://localhost:3000
|
||||||
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
|
|
||||||
# Fallbacks accepted too:
|
|
||||||
# VITE_SUPABASE_URL=https://your-project-ref.supabase.co
|
|
||||||
# VITE_SUPABASE_ANON_KEY=your-anon-key
|
|
||||||
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
|
CORS_ORIGIN=http://localhost:5173,http://127.0.0.1:5173
|
||||||
LOG_LEVEL=debug
|
LOG_LEVEL=debug
|
||||||
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/monorepo
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/monorepo
|
||||||
@@ -16,3 +12,7 @@ SMTP_PORT=587
|
|||||||
SMTP_USER=user@example.com
|
SMTP_USER=user@example.com
|
||||||
SMTP_PASS=your-smtp-password
|
SMTP_PASS=your-smtp-password
|
||||||
SMTP_FROM=Playzer <no-reply@example.com>
|
SMTP_FROM=Playzer <no-reply@example.com>
|
||||||
|
|
||||||
|
# Google OAuth
|
||||||
|
GOOGLE_CLIENT_ID=your-google-client-id
|
||||||
|
GOOGLE_CLIENT_SECRET=your-google-client-secret
|
||||||
|
|||||||
1
apps/backend/.gitignore
vendored
1
apps/backend/.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
# deps
|
# deps
|
||||||
node_modules/
|
node_modules/
|
||||||
|
prisma.config.prod.ts
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# syntax=docker/dockerfile:1.7
|
|
||||||
|
|
||||||
FROM oven/bun:1.3 AS deps
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# Workspace manifests first for better layer caching.
|
|
||||||
COPY package.json bun.lock ./
|
|
||||||
COPY apps/backend/package.json apps/backend/package.json
|
|
||||||
COPY packages/api-contract/package.json packages/api-contract/package.json
|
|
||||||
|
|
||||||
RUN bun install --frozen-lockfile
|
|
||||||
|
|
||||||
FROM deps AS build
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY apps/backend apps/backend
|
|
||||||
COPY packages/api-contract packages/api-contract
|
|
||||||
|
|
||||||
# Prisma 7 config resolves DATABASE_URL at generate time.
|
|
||||||
ARG DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
|
||||||
ENV DATABASE_URL=${DATABASE_URL}
|
|
||||||
|
|
||||||
RUN bun --cwd apps/backend run prisma:generate
|
|
||||||
|
|
||||||
FROM oven/bun:1.3-slim AS runner
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
|
||||||
ENV HOST=0.0.0.0
|
|
||||||
ENV PORT=3000
|
|
||||||
|
|
||||||
COPY --from=deps /app/node_modules ./node_modules
|
|
||||||
COPY --from=build /app/apps/backend ./apps/backend
|
|
||||||
COPY --from=build /app/packages ./packages
|
|
||||||
COPY package.json bun.lock ./
|
|
||||||
|
|
||||||
WORKDIR /app/apps/backend
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
CMD ["bun", "run", "start"]
|
|
||||||
@@ -9,3 +9,21 @@ bun run dev
|
|||||||
```
|
```
|
||||||
|
|
||||||
open http://localhost:3000
|
open http://localhost:3000
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Run all backend tests:
|
||||||
|
```sh
|
||||||
|
bun run test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test naming convention
|
||||||
|
|
||||||
|
- `*.test.ts` or `*.spec.ts`: service-level tests. These run with the shared Prisma preload at `test/support/prisma.mock.ts`.
|
||||||
|
- `*.handler.test.ts`: handler-level tests. These should mock the service module locally with `mock.module(...)` and import the handler after the mock is in place.
|
||||||
|
|
||||||
|
### Recommended pattern
|
||||||
|
|
||||||
|
- Keep business logic tests focused on the service layer.
|
||||||
|
- Use handler tests only to verify request parsing, status mapping, and response shaping.
|
||||||
|
- Put shared Prisma mocking helpers in `test/support/`.
|
||||||
|
|||||||
26
apps/backend/bun.lock
Normal file
26
apps/backend/bun.lock
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "backend",
|
||||||
|
"dependencies": {
|
||||||
|
"hono": "^4.12.9",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
|
||||||
|
|
||||||
|
"hono": ["hono@4.12.9", "", {}, "sha512-wy3T8Zm2bsEvxKZM5w21VdHDDcwVS1yUFFY6i8UobSsKfFceT7TOwhbhfKsDyx7tYQlmRM5FLpIuYvNFyjctiA=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
3
apps/backend/bunfig.toml
Normal file
3
apps/backend/bunfig.toml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
[test]
|
||||||
|
preload = ["./test/support/prisma.mock.ts"]
|
||||||
|
coverageThreshold = 0.8
|
||||||
@@ -3,29 +3,38 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bun run --hot src/server.ts",
|
"dev": "bun run --hot src/server.ts",
|
||||||
"start": "bun src/server.ts",
|
"start": "bun src/server.ts",
|
||||||
|
"build": "tsc -b",
|
||||||
|
"test": "bun test --preload ./test/support/prisma.mock.ts ./test",
|
||||||
|
"lint": "biome check .",
|
||||||
|
"lint:fix": "biome check --write .",
|
||||||
|
"format": "biome format --write .",
|
||||||
"prisma:generate": "prisma generate",
|
"prisma:generate": "prisma generate",
|
||||||
"prisma:migrate": "prisma migrate dev",
|
"prisma:migrate": "prisma migrate dev",
|
||||||
|
"prisma:migrate:deploy": "prisma migrate deploy",
|
||||||
"prisma:seed": "prisma db seed",
|
"prisma:seed": "prisma db seed",
|
||||||
"prisma:seed:reset:plans": "bun prisma/reset-plans.ts"
|
"prisma:seed:reset:plans": "bun prisma/reset-plans.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@better-auth/infra": "^0.2.4",
|
||||||
"@hono/zod-validator": "^0.7.6",
|
"@hono/zod-validator": "^0.7.6",
|
||||||
"@prisma/adapter-pg": "^7.6.0",
|
"@prisma/adapter-pg": "^7.6.0",
|
||||||
"@prisma/client": "^7",
|
"@prisma/client": "^7",
|
||||||
"@repo/api-contract": "workspace:*",
|
"@repo/api-contract": "workspace:*",
|
||||||
"@supabase/supabase-js": "2.101.1",
|
"better-auth": "^1.6.4",
|
||||||
"dotenv": "^17.4.1",
|
"dotenv": "^17.4.1",
|
||||||
"hono": "4.12.10",
|
"hono": "4.12.10",
|
||||||
|
"mailtrap": "^4.5.1",
|
||||||
"nodemailer": "^8.0.5",
|
"nodemailer": "^8.0.5",
|
||||||
"pg": "^8.20.0",
|
"pg": "^8.20.0",
|
||||||
"pino": "10.3.1",
|
"pino": "10.3.1",
|
||||||
"pino-pretty": "13.1.3",
|
"pino-pretty": "13.1.3",
|
||||||
"pino-std-serializers": "7.1.0",
|
"pino-std-serializers": "7.1.0",
|
||||||
|
"prisma": "^7",
|
||||||
"uuid": "^13.0.0"
|
"uuid": "^13.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/bun": "latest",
|
"@types/bun": "latest",
|
||||||
"@types/nodemailer": "^8.0.0",
|
"@types/nodemailer": "^8.0.0",
|
||||||
"prisma": "^7"
|
"bun-mock-prisma": "^1.2.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config';
|
||||||
import { defineConfig, env } from 'prisma/config'
|
import { defineConfig, env } from 'prisma/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
schema: 'prisma',
|
schema: 'prisma',
|
||||||
@@ -10,4 +10,4 @@ export default defineConfig({
|
|||||||
datasource: {
|
datasource: {
|
||||||
url: env('DATABASE_URL'),
|
url: env('DATABASE_URL'),
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
65
apps/backend/prisma/auth.prisma
Normal file
65
apps/backend/prisma/auth.prisma
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
model User {
|
||||||
|
id String @id
|
||||||
|
name String
|
||||||
|
email String
|
||||||
|
emailVerified Boolean @default(false)
|
||||||
|
image String?
|
||||||
|
phone String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
sessions Session[]
|
||||||
|
accounts Account[]
|
||||||
|
role String @default("member")
|
||||||
|
complexes ComplexUser[]
|
||||||
|
|
||||||
|
@@unique([email])
|
||||||
|
@@map("users")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Session {
|
||||||
|
id String @id
|
||||||
|
expiresAt DateTime
|
||||||
|
token String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
ipAddress String?
|
||||||
|
userAgent String?
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@unique([token])
|
||||||
|
@@index([userId])
|
||||||
|
@@map("sessions")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Account {
|
||||||
|
id String @id
|
||||||
|
accountId String
|
||||||
|
providerId String
|
||||||
|
userId String
|
||||||
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
|
accessToken String?
|
||||||
|
refreshToken String?
|
||||||
|
idToken String?
|
||||||
|
accessTokenExpiresAt DateTime?
|
||||||
|
refreshTokenExpiresAt DateTime?
|
||||||
|
scope String?
|
||||||
|
password String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@index([userId])
|
||||||
|
@@map("accounts")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Verification {
|
||||||
|
id String @id
|
||||||
|
identifier String
|
||||||
|
value String
|
||||||
|
expiresAt DateTime
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
|
||||||
|
@@index([identifier])
|
||||||
|
@@map("verification")
|
||||||
|
}
|
||||||
@@ -2,6 +2,9 @@ model Complex {
|
|||||||
id String @id @db.Uuid
|
id String @id @db.Uuid
|
||||||
complexName String @map("complex_name")
|
complexName String @map("complex_name")
|
||||||
physicalAddress String? @map("physical_address") @db.VarChar(200)
|
physicalAddress String? @map("physical_address") @db.VarChar(200)
|
||||||
|
city String? @map("city") @db.VarChar(100)
|
||||||
|
state String? @map("state") @db.VarChar(100)
|
||||||
|
country String? @map("country") @db.VarChar(100)
|
||||||
complexSlug String @unique @map("complex_slug")
|
complexSlug String @unique @map("complex_slug")
|
||||||
adminEmail String @map("admin_email")
|
adminEmail String @map("admin_email")
|
||||||
planCode String? @map("plan_code") @db.VarChar(10)
|
planCode String? @map("plan_code") @db.VarChar(10)
|
||||||
@@ -9,6 +12,7 @@ model Complex {
|
|||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
plan Plan? @relation(fields: [planCode], references: [code])
|
plan Plan? @relation(fields: [planCode], references: [code])
|
||||||
users ComplexUser[]
|
users ComplexUser[]
|
||||||
|
invitations ComplexInvitation[]
|
||||||
courts Court[]
|
courts Court[]
|
||||||
|
|
||||||
@@index([planCode])
|
@@index([planCode])
|
||||||
@@ -18,11 +22,12 @@ model Complex {
|
|||||||
|
|
||||||
enum ComplexUserRole {
|
enum ComplexUserRole {
|
||||||
ADMIN
|
ADMIN
|
||||||
|
EMPLOYEE
|
||||||
}
|
}
|
||||||
|
|
||||||
model ComplexUser {
|
model ComplexUser {
|
||||||
complexId String @map("complex_id") @db.Uuid
|
complexId String @map("complex_id") @db.Uuid
|
||||||
userId String @map("user_id") @db.Uuid
|
userId String @map("user_id")
|
||||||
role ComplexUserRole @default(ADMIN)
|
role ComplexUserRole @default(ADMIN)
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
||||||
@@ -32,3 +37,20 @@ model ComplexUser {
|
|||||||
@@index([userId])
|
@@index([userId])
|
||||||
@@map("complex_users")
|
@@map("complex_users")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model ComplexInvitation {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
complexId String @map("complex_id") @db.Uuid
|
||||||
|
email String @db.VarChar(255)
|
||||||
|
tokenHash String @unique @map("token_hash")
|
||||||
|
expiresAt DateTime @map("expires_at")
|
||||||
|
acceptedAt DateTime? @map("accepted_at")
|
||||||
|
revokedAt DateTime? @map("revoked_at")
|
||||||
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
complex Complex @relation(fields: [complexId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@index([complexId, email])
|
||||||
|
@@index([email])
|
||||||
|
@@map("complex_invitations")
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ enum DayOfWeek {
|
|||||||
enum CourtBookingStatus {
|
enum CourtBookingStatus {
|
||||||
CONFIRMED
|
CONFIRMED
|
||||||
CANCELLED
|
CANCELLED
|
||||||
|
COMPLETED
|
||||||
|
NOSHOW
|
||||||
}
|
}
|
||||||
|
|
||||||
model Sport {
|
model Sport {
|
||||||
@@ -83,6 +85,7 @@ model CourtBooking {
|
|||||||
endTime String @map("end_time") @db.VarChar(5)
|
endTime String @map("end_time") @db.VarChar(5)
|
||||||
customerName String @map("customer_name") @db.VarChar(120)
|
customerName String @map("customer_name") @db.VarChar(120)
|
||||||
customerPhone String @map("customer_phone") @db.VarChar(30)
|
customerPhone String @map("customer_phone") @db.VarChar(30)
|
||||||
|
customerEmail String? @map("customer_email") @db.VarChar(254)
|
||||||
status CourtBookingStatus @default(CONFIRMED)
|
status CourtBookingStatus @default(CONFIRMED)
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
@@ -93,3 +96,21 @@ model CourtBooking {
|
|||||||
@@index([bookingDate])
|
@@index([bookingDate])
|
||||||
@@map("court_bookings")
|
@@map("court_bookings")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
model CourtBookingLog {
|
||||||
|
id String @id @db.Uuid
|
||||||
|
bookingCode String @map("booking_code") @db.VarChar(8)
|
||||||
|
courtId String @map("court_id") @db.Uuid
|
||||||
|
bookingDate DateTime @map("booking_date") @db.Date
|
||||||
|
startTime String @map("start_time") @db.VarChar(5)
|
||||||
|
endTime String @map("end_time") @db.VarChar(5)
|
||||||
|
previousStatus CourtBookingStatus @map("previous_status")
|
||||||
|
newStatus CourtBookingStatus @map("new_status")
|
||||||
|
customerName String @map("customer_name") @db.VarChar(120)
|
||||||
|
customerPhone String @map("customer_phone") @db.VarChar(30)
|
||||||
|
customerEmail String? @map("customer_email") @db.VarChar(254)
|
||||||
|
changedAt DateTime @default(now()) @map("changed_at")
|
||||||
|
|
||||||
|
@@map("court_booking_logs")
|
||||||
|
@@index([courtId, newStatus])
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
ALTER TYPE "CourtBookingStatus" ADD VALUE 'COMPLETED';
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "complexes" ADD COLUMN "city" VARCHAR(100),
|
||||||
|
ADD COLUMN "country" VARCHAR(100),
|
||||||
|
ADD COLUMN "state" VARCHAR(100);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "password_reset_requests" (
|
||||||
|
"id" UUID NOT NULL,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"otp_hash" TEXT NOT NULL,
|
||||||
|
"otp_expires_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
"otp_attempts" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"otp_last_sent_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
"otp_resend_count" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"used_at" TIMESTAMP(3),
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "password_reset_requests_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "password_reset_requests_email_idx" ON "password_reset_requests"("email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "password_reset_requests_otp_expires_at_idx" ON "password_reset_requests"("otp_expires_at");
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- The primary key for the `users` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||||
|
- You are about to drop the column `created_at` on the `users` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `full_name` on the `users` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `supabase_user_id` on the `users` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `updated_at` on the `users` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the `complex_users` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- Added the required column `name` to the `users` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `updatedAt` to the `users` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterEnum
|
||||||
|
ALTER TYPE "ComplexUserRole" ADD VALUE 'EMPLOYEE';
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "complex_users" DROP CONSTRAINT "complex_users_complex_id_fkey";
|
||||||
|
|
||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE "complex_users" DROP CONSTRAINT "complex_users_user_id_fkey";
|
||||||
|
|
||||||
|
-- DropIndex
|
||||||
|
DROP INDEX "users_supabase_user_id_key";
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" DROP CONSTRAINT "users_pkey",
|
||||||
|
DROP COLUMN "created_at",
|
||||||
|
DROP COLUMN "full_name",
|
||||||
|
DROP COLUMN "supabase_user_id",
|
||||||
|
DROP COLUMN "updated_at",
|
||||||
|
ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
ADD COLUMN "emailVerified" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
ADD COLUMN "image" TEXT,
|
||||||
|
ADD COLUMN "name" TEXT NOT NULL,
|
||||||
|
ADD COLUMN "updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
ALTER COLUMN "id" SET DATA TYPE TEXT,
|
||||||
|
ADD CONSTRAINT "users_pkey" PRIMARY KEY ("id");
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "complex_users";
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "sessions" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"token" TEXT NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"ipAddress" TEXT,
|
||||||
|
"userAgent" TEXT,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "sessions_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "accounts" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"accountId" TEXT NOT NULL,
|
||||||
|
"providerId" TEXT NOT NULL,
|
||||||
|
"userId" TEXT NOT NULL,
|
||||||
|
"accessToken" TEXT,
|
||||||
|
"refreshToken" TEXT,
|
||||||
|
"idToken" TEXT,
|
||||||
|
"accessTokenExpiresAt" TIMESTAMP(3),
|
||||||
|
"refreshTokenExpiresAt" TIMESTAMP(3),
|
||||||
|
"scope" TEXT,
|
||||||
|
"password" TEXT,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "accounts_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "verification" (
|
||||||
|
"id" TEXT NOT NULL,
|
||||||
|
"identifier" TEXT NOT NULL,
|
||||||
|
"value" TEXT NOT NULL,
|
||||||
|
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "sessions_userId_idx" ON "sessions"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "sessions_token_key" ON "sessions"("token");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "accounts_userId_idx" ON "accounts"("userId");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "accounts" ADD CONSTRAINT "accounts_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "complex_users" (
|
||||||
|
"complex_id" UUID NOT NULL,
|
||||||
|
"user_id" TEXT NOT NULL,
|
||||||
|
"role" "ComplexUserRole" NOT NULL DEFAULT 'ADMIN',
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "complex_users_pkey" PRIMARY KEY ("complex_id","user_id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "complex_users_user_id_idx" ON "complex_users"("user_id");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "complex_users" ADD CONSTRAINT "complex_users_complex_id_fkey" FOREIGN KEY ("complex_id") REFERENCES "complexes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "complex_users" ADD CONSTRAINT "complex_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" ADD COLUMN "role" TEXT NOT NULL DEFAULT 'member';
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "complex_invitations" (
|
||||||
|
"id" UUID NOT NULL,
|
||||||
|
"complex_id" UUID NOT NULL,
|
||||||
|
"email" VARCHAR(255) NOT NULL,
|
||||||
|
"token_hash" TEXT NOT NULL,
|
||||||
|
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
"accepted_at" TIMESTAMP(3),
|
||||||
|
"revoked_at" TIMESTAMP(3),
|
||||||
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||||
|
|
||||||
|
CONSTRAINT "complex_invitations_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "complex_invitations_token_hash_key" ON "complex_invitations"("token_hash");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "complex_invitations_complex_id_email_idx" ON "complex_invitations"("complex_id", "email");
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "complex_invitations_email_idx" ON "complex_invitations"("email");
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "complex_invitations" ADD CONSTRAINT "complex_invitations_complex_id_fkey" FOREIGN KEY ("complex_id") REFERENCES "complexes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "court_booking_logs" (
|
||||||
|
"id" UUID NOT NULL,
|
||||||
|
"booking_code" UUID NOT NULL,
|
||||||
|
"court_id" UUID NOT NULL,
|
||||||
|
"booking_date" DATE NOT NULL,
|
||||||
|
"start_time" VARCHAR(5) NOT NULL,
|
||||||
|
"end_time" VARCHAR(5) NOT NULL,
|
||||||
|
"previous_status" "CourtBookingStatus" NOT NULL,
|
||||||
|
"new_status" "CourtBookingStatus" NOT NULL,
|
||||||
|
"changed_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
|
||||||
|
CONSTRAINT "court_booking_logs_pkey" PRIMARY KEY ("id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE INDEX "court_booking_logs_court_id_new_status_idx" ON "court_booking_logs"("court_id", "new_status");
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `customer_name` to the `court_booking_logs` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `customer_phone` to the `court_booking_logs` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterEnum
|
||||||
|
ALTER TYPE "CourtBookingStatus" ADD VALUE 'NOSHOW';
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "court_booking_logs" ADD COLUMN "customer_name" VARCHAR(120) NOT NULL,
|
||||||
|
ADD COLUMN "customer_phone" VARCHAR(30) NOT NULL;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Changed the type of `booking_code` on the `court_booking_logs` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "court_booking_logs" DROP COLUMN "booking_code",
|
||||||
|
ADD COLUMN "booking_code" VARCHAR(8) NOT NULL;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "users" ADD COLUMN "phone" TEXT;
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `onboarding_requests` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "court_booking_logs" ADD COLUMN "customer_email" VARCHAR(254);
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "court_bookings" ADD COLUMN "customer_email" VARCHAR(254);
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE "onboarding_requests";
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
model OnboardingRequest {
|
model PasswordResetRequest {
|
||||||
id String @id @db.Uuid
|
id String @id @db.Uuid
|
||||||
fullName String @map("full_name")
|
|
||||||
email String
|
email String
|
||||||
otpHash String @map("otp_hash")
|
otpHash String @map("otp_hash")
|
||||||
otpExpiresAt DateTime @map("otp_expires_at")
|
otpExpiresAt DateTime @map("otp_expires_at")
|
||||||
otpAttempts Int @default(0) @map("otp_attempts")
|
otpAttempts Int @default(0) @map("otp_attempts")
|
||||||
otpLastSentAt DateTime @map("otp_last_sent_at")
|
otpLastSentAt DateTime @map("otp_last_sent_at")
|
||||||
otpResendCount Int @default(0) @map("otp_resend_count")
|
otpResendCount Int @default(0) @map("otp_resend_count")
|
||||||
emailVerifiedAt DateTime? @map("email_verified_at")
|
usedAt DateTime? @map("used_at")
|
||||||
completedAt DateTime? @map("completed_at")
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
createdAt DateTime @default(now()) @map("created_at")
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
@@index([email])
|
@@index([email])
|
||||||
@@index([otpExpiresAt])
|
@@index([otpExpiresAt])
|
||||||
@@map("onboarding_requests")
|
@@map("password_reset_requests")
|
||||||
}
|
}
|
||||||
@@ -74,4 +74,4 @@ export const planSeeds = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] as const
|
] as const;
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config';
|
||||||
import { PrismaPg } from '@prisma/adapter-pg'
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
import { PrismaClient } from '../src/generated/prisma/client'
|
import { PrismaClient } from '../src/generated/prisma/client';
|
||||||
import { planSeeds } from './plans.seed-data'
|
import { planSeeds } from './plans.seed-data';
|
||||||
|
|
||||||
const databaseUrl = process.env.DATABASE_URL
|
const databaseUrl = process.env.DATABASE_URL;
|
||||||
|
|
||||||
if (!databaseUrl) {
|
if (!databaseUrl) {
|
||||||
throw new Error('Missing DATABASE_URL in environment for plan reset.')
|
throw new Error('Missing DATABASE_URL in environment for plan reset.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = new PrismaPg({
|
const adapter = new PrismaPg({
|
||||||
connectionString: databaseUrl,
|
connectionString: databaseUrl,
|
||||||
})
|
});
|
||||||
|
|
||||||
const prisma = new PrismaClient({ adapter })
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
const complexesUsingPlans = await prisma.complex.count({
|
const complexesUsingPlans = await prisma.complex.count({
|
||||||
where: { planCode: { not: null } },
|
where: { planCode: { not: null } },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (complexesUsingPlans > 0) {
|
if (complexesUsingPlans > 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No se puede resetear planes: hay ${complexesUsingPlans} complejos con plan asignado.`,
|
`No se puede resetear planes: hay ${complexesUsingPlans} complejos con plan asignado.`
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.$transaction(async (tx) => {
|
await prisma.$transaction(async (tx) => {
|
||||||
await tx.plan.deleteMany({})
|
await tx.plan.deleteMany({});
|
||||||
|
|
||||||
for (const plan of planSeeds) {
|
for (const plan of planSeeds) {
|
||||||
await tx.plan.create({
|
await tx.plan.create({
|
||||||
@@ -37,19 +37,19 @@ async function run() {
|
|||||||
price: plan.price,
|
price: plan.price,
|
||||||
rules: plan.rules,
|
rules: plan.rules,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
console.log('Planes reseteados: BASIC, ADVANCED, ENTERPRISE')
|
console.log('Planes reseteados: BASIC, ADVANCED, ENTERPRISE');
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
})
|
})
|
||||||
.catch(async (error) => {
|
.catch(async (error) => {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
process.exit(1)
|
process.exit(1);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import 'dotenv/config'
|
import 'dotenv/config';
|
||||||
import { PrismaPg } from '@prisma/adapter-pg'
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
import { v7 as uuidv7 } from 'uuid'
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
import { PrismaClient } from '../src/generated/prisma/client'
|
import { PrismaClient } from '../src/generated/prisma/client';
|
||||||
import { planSeeds } from './plans.seed-data'
|
import { planSeeds } from './plans.seed-data';
|
||||||
import { sportSeeds } from './sports.seed-data'
|
import { sportSeeds } from './sports.seed-data';
|
||||||
|
|
||||||
const databaseUrl = process.env.DATABASE_URL
|
const databaseUrl = process.env.DATABASE_URL;
|
||||||
|
|
||||||
if (!databaseUrl) {
|
if (!databaseUrl) {
|
||||||
throw new Error('Missing DATABASE_URL in environment for seeding.')
|
throw new Error('Missing DATABASE_URL in environment for seeding.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = new PrismaPg({
|
const adapter = new PrismaPg({
|
||||||
connectionString: databaseUrl,
|
connectionString: databaseUrl,
|
||||||
})
|
});
|
||||||
|
|
||||||
const prisma = new PrismaClient({ adapter })
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
for (const plan of planSeeds) {
|
for (const plan of planSeeds) {
|
||||||
@@ -33,7 +33,7 @@ async function run() {
|
|||||||
price: plan.price,
|
price: plan.price,
|
||||||
rules: plan.rules,
|
rules: plan.rules,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const sport of sportSeeds) {
|
for (const sport of sportSeeds) {
|
||||||
@@ -49,16 +49,16 @@ async function run() {
|
|||||||
slug: sport.slug,
|
slug: sport.slug,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run()
|
run()
|
||||||
.then(async () => {
|
.then(async () => {
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
})
|
})
|
||||||
.catch(async (error) => {
|
.catch(async (error) => {
|
||||||
console.error(error)
|
console.error(error);
|
||||||
await prisma.$disconnect()
|
await prisma.$disconnect();
|
||||||
process.exit(1)
|
process.exit(1);
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ export const sportSeeds = [
|
|||||||
{ name: 'Pickleball', slug: 'pickleball' },
|
{ name: 'Pickleball', slug: 'pickleball' },
|
||||||
{ name: 'Basquet', slug: 'basquet' },
|
{ name: 'Basquet', slug: 'basquet' },
|
||||||
{ name: 'Basquet 3', slug: 'basquet-3' },
|
{ name: 'Basquet 3', slug: 'basquet-3' },
|
||||||
] as const
|
] as const;
|
||||||
|
|||||||
@@ -1,11 +1 @@
|
|||||||
model User {
|
// User model is defined in auth.prisma (Better Auth schema).
|
||||||
id String @id @db.Uuid
|
|
||||||
supabaseUserId String @unique @map("supabase_user_id") @db.Uuid
|
|
||||||
email String @unique
|
|
||||||
fullName String @map("full_name")
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
|
||||||
complexes ComplexUser[]
|
|
||||||
|
|
||||||
@@map("users")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,36 +1,41 @@
|
|||||||
import { Hono } from 'hono'
|
import { logger } from '@/lib/logger';
|
||||||
import { cors } from 'hono/cors'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { logger } from '@/lib/logger'
|
import { Hono } from 'hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { cors } from 'hono/cors';
|
||||||
import { requestId } from 'hono/request-id'
|
import { requestId } from 'hono/request-id';
|
||||||
|
import { errorHandler } from './lib/http/error-handler';
|
||||||
|
import { requestIdMiddleware } from './lib/http/request-id';
|
||||||
|
|
||||||
export function createApp() {
|
export function createApp() {
|
||||||
const app = new Hono<AppEnv>()
|
const app = new Hono<AppEnv>();
|
||||||
|
|
||||||
const allowedOrigins = (
|
app.use('*', requestIdMiddleware);
|
||||||
Bun.env.CORS_ORIGIN ??
|
app.use('*', errorHandler);
|
||||||
'http://localhost:5173,http://127.0.0.1:5173'
|
|
||||||
)
|
const allowedOrigins = (Bun.env.CORS_ORIGIN ?? 'http://localhost:5173,http://127.0.0.1:5173')
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((value) => value.trim())
|
.map((value) => value.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean);
|
||||||
|
|
||||||
app.use(
|
app.use(
|
||||||
'*',
|
'*',
|
||||||
cors({
|
cors({
|
||||||
origin: (origin) => {
|
origin: (origin) => {
|
||||||
if (!origin) return allowedOrigins[0] ?? ''
|
if (!origin) return allowedOrigins[0] ?? '';
|
||||||
return allowedOrigins.includes(origin) ? origin : ''
|
return allowedOrigins.includes(origin) ? origin : '';
|
||||||
},
|
},
|
||||||
allowHeaders: ['Authorization', 'Content-Type'],
|
allowHeaders: ['Authorization', 'Content-Type'],
|
||||||
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||||
}),
|
exposeHeaders: ['Content-Length'],
|
||||||
)
|
maxAge: 600,
|
||||||
|
credentials: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
app.use('*', async (c, next) => {
|
app.use('*', async (c, next) => {
|
||||||
const start = performance.now()
|
const start = performance.now();
|
||||||
await next()
|
await next();
|
||||||
const durationMs = Number((performance.now() - start).toFixed(1))
|
const durationMs = Number((performance.now() - start).toFixed(1));
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
{
|
{
|
||||||
@@ -38,11 +43,11 @@ export function createApp() {
|
|||||||
path: c.req.path,
|
path: c.req.path,
|
||||||
status: c.res.status,
|
status: c.res.status,
|
||||||
durationMs,
|
durationMs,
|
||||||
requestId
|
requestId,
|
||||||
},
|
},
|
||||||
'http_request',
|
'http_request'
|
||||||
)
|
);
|
||||||
})
|
});
|
||||||
|
|
||||||
return app
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
385
apps/backend/src/emails/booking-confirmation.ts
Normal file
385
apps/backend/src/emails/booking-confirmation.ts
Normal file
@@ -0,0 +1,385 @@
|
|||||||
|
type BookingEmailData = {
|
||||||
|
bookingCode: string;
|
||||||
|
complexName: string;
|
||||||
|
date: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
courtName: string;
|
||||||
|
sportName: string;
|
||||||
|
customerName: string;
|
||||||
|
price?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const APP_BASE_URL = process.env.APP_BASE_URL ?? 'http://localhost:5173';
|
||||||
|
|
||||||
|
function formatBookingPrice(price: number): string {
|
||||||
|
if (price === 0) {
|
||||||
|
return 'Sin cargo';
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Intl.NumberFormat('es-AR', {
|
||||||
|
style: 'currency',
|
||||||
|
currency: 'ARS',
|
||||||
|
maximumFractionDigits: Number.isInteger(price) ? 0 : 2,
|
||||||
|
}).format(price);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFriendlyDate(isoDate: string): string {
|
||||||
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(isoDate);
|
||||||
|
if (!match) return isoDate;
|
||||||
|
|
||||||
|
const date = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
||||||
|
|
||||||
|
const weekday = new Intl.DateTimeFormat('es-AR', { weekday: 'long' }).format(date);
|
||||||
|
const day = date.getDate();
|
||||||
|
const month = new Intl.DateTimeFormat('es-AR', { month: 'long' }).format(date);
|
||||||
|
|
||||||
|
return `${weekday.charAt(0).toUpperCase() + weekday.slice(1)}, ${day} de ${month}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BASE_STYLES = `
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #edf7f4;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||||
|
}
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export function wrapLayout(content: string) {
|
||||||
|
return `<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Playzer</title>
|
||||||
|
<style>${BASE_STYLES}</style>
|
||||||
|
</head>
|
||||||
|
<body style="margin:0;padding:0;background-color:#edf7f4;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#edf7f4;min-height:100vh;">
|
||||||
|
<tr>
|
||||||
|
<td align="center" style="padding:32px 16px;">
|
||||||
|
<table role="presentation" width="100%" style="max-width:520px;background-color:#ffffff;border-radius:28px;overflow:hidden;box-shadow:0 24px 70px rgba(15,23,42,0.12);border:1px solid rgba(5,9,20,0.1);">
|
||||||
|
${content}
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bookingConfirmationHtml(data: BookingEmailData): string {
|
||||||
|
const formattedPrice = formatBookingPrice(data.price ?? 0);
|
||||||
|
const friendlyDate = formatFriendlyDate(data.date);
|
||||||
|
|
||||||
|
const content = `
|
||||||
|
<tr>
|
||||||
|
<td style="padding:32px 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="top">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;background-color:#f0fdf4;border-radius:999px;padding:4px 12px;">
|
||||||
|
<tr>
|
||||||
|
<td style="font-size:11px;font-weight:700;letter-spacing:0.14em;color:#15803d;text-transform:uppercase;line-height:1.25rem;">
|
||||||
|
✓ Reserva confirmada
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h1 style="margin:12px 0 0;font-size:28px;font-weight:700;color:#111827;letter-spacing:-0.025em;">
|
||||||
|
${data.complexName}
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
<td valign="top" align="right" style="white-space:nowrap;">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="middle" style="padding-right:8px;">
|
||||||
|
<img src="${APP_BASE_URL}/playzer-favicon-512-transparent.png" alt="Playzer" width="32" height="32" style="display:block;" />
|
||||||
|
</td>
|
||||||
|
<td valign="middle">
|
||||||
|
<span style="font-size:18px;font-weight:700;color:#475569;letter-spacing:-0.025em;">Playzer</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f0fdf4;border-radius:24px;border:1px solid rgba(5,150,105,0.3);">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="bottom">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:600;color:#15803d;">
|
||||||
|
Tu turno
|
||||||
|
</p>
|
||||||
|
<p style="margin:12px 0 0;font-size:28px;font-weight:900;color:#111827;line-height:1.1;letter-spacing:-0.025em;">
|
||||||
|
${friendlyDate}
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:24px;font-weight:900;color:#059669;line-height:1;letter-spacing:-0.025em;">
|
||||||
|
${data.startTime} — ${data.endTime}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
<td valign="bottom" align="right" style="padding-left:16px;">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0" style="background-color:rgba(255,255,255,0.8);border-radius:16px;border:1px solid rgba(5,150,105,0.2);">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:12px 16px;text-align:right;min-width:120px;">
|
||||||
|
<p style="margin:0;font-size:11px;font-weight:500;color:#6b7280;">Precio del turno</p>
|
||||||
|
<p style="margin:4px 0 0;font-size:20px;font-weight:700;color:#111827;">${formattedPrice}</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border:1px solid #e5e7eb;border-radius:22px;overflow:hidden;">
|
||||||
|
<tr>
|
||||||
|
<td width="50%" style="padding:16px;border-right:1px solid #e5e7eb;background-color:#ffffff;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||||
|
Cancha
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||||
|
${data.courtName} — ${data.sportName}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
<td width="50%" style="padding:16px;background-color:#ffffff;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||||
|
Código de reserva
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-family:monospace;font-size:16px;font-weight:700;letter-spacing:0.18em;color:#111827;">
|
||||||
|
${data.bookingCode}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 32px;">
|
||||||
|
<p style="margin:0;font-size:13px;color:#6b7280;text-align:center;line-height:1.5;">
|
||||||
|
Presentá el código de reserva al llegar al complejo.
|
||||||
|
<br />
|
||||||
|
Si necesitás cancelar o modificar, contactate directamente con el complejo.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
return wrapLayout(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bookingCancelledHtml(data: BookingEmailData): string {
|
||||||
|
const friendlyDate = formatFriendlyDate(data.date);
|
||||||
|
|
||||||
|
const content = `
|
||||||
|
<tr>
|
||||||
|
<td style="padding:32px 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="top">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;background-color:#fef2f2;border-radius:999px;padding:4px 12px;">
|
||||||
|
<tr>
|
||||||
|
<td style="font-size:11px;font-weight:700;letter-spacing:0.14em;color:#dc2626;text-transform:uppercase;line-height:1.25rem;">
|
||||||
|
✗ Reserva cancelada
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h1 style="margin:12px 0 0;font-size:28px;font-weight:700;color:#111827;letter-spacing:-0.025em;">
|
||||||
|
${data.complexName}
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
<td valign="top" align="right" style="white-space:nowrap;">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="middle" style="padding-right:8px;">
|
||||||
|
<img src="${APP_BASE_URL}/playzer-favicon-512-transparent.png" alt="Playzer" width="32" height="32" style="display:block;" />
|
||||||
|
</td>
|
||||||
|
<td valign="middle">
|
||||||
|
<span style="font-size:18px;font-weight:700;color:#475569;letter-spacing:-0.025em;">Playzer</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f0fdf4;border-radius:24px;border:1px solid rgba(5,150,105,0.3);">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px 24px;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:600;color:#15803d;">
|
||||||
|
Tu turno
|
||||||
|
</p>
|
||||||
|
<p style="margin:12px 0 0;font-size:28px;font-weight:900;color:#111827;line-height:1.1;letter-spacing:-0.025em;">
|
||||||
|
${friendlyDate}
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:24px;font-weight:900;color:#059669;line-height:1;letter-spacing:-0.025em;">
|
||||||
|
${data.startTime} — ${data.endTime}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#fef2f2;border-radius:24px;border:1px solid rgba(220,38,38,0.3);">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px 24px;">
|
||||||
|
<p style="margin:0;font-size:14px;color:#374151;line-height:1.6;">
|
||||||
|
La reserva <strong style="font-family:monospace;font-weight:700;letter-spacing:2px;">${data.bookingCode}</strong>
|
||||||
|
fue cancelada. Si tenés dudas, contactate con el complejo.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 32px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border:1px solid #e5e7eb;border-radius:22px;overflow:hidden;">
|
||||||
|
<tr>
|
||||||
|
<td width="50%" style="padding:16px;border-right:1px solid #e5e7eb;background-color:#ffffff;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||||
|
Cancha
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||||
|
${data.courtName} — ${data.sportName}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
<td width="50%" style="padding:16px;background-color:#ffffff;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||||
|
Cliente
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||||
|
${data.customerName}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
return wrapLayout(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function bookingNoShowHtml(data: BookingEmailData): string {
|
||||||
|
const friendlyDate = formatFriendlyDate(data.date);
|
||||||
|
|
||||||
|
const content = `
|
||||||
|
<tr>
|
||||||
|
<td style="padding:32px 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="top">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;background-color:#fffbeb;border-radius:999px;padding:4px 12px;">
|
||||||
|
<tr>
|
||||||
|
<td style="font-size:11px;font-weight:700;letter-spacing:0.14em;color:#d97706;text-transform:uppercase;line-height:1.25rem;">
|
||||||
|
Reserva no concretada
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h1 style="margin:12px 0 0;font-size:28px;font-weight:700;color:#111827;letter-spacing:-0.025em;">
|
||||||
|
${data.complexName}
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
<td valign="top" align="right" style="white-space:nowrap;">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="middle" style="padding-right:8px;">
|
||||||
|
<img src="${APP_BASE_URL}/playzer-favicon-512-transparent.png" alt="Playzer" width="32" height="32" style="display:block;" />
|
||||||
|
</td>
|
||||||
|
<td valign="middle">
|
||||||
|
<span style="font-size:18px;font-weight:700;color:#475569;letter-spacing:-0.025em;">Playzer</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#f0fdf4;border-radius:24px;border:1px solid rgba(5,150,105,0.3);">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px 24px;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:600;color:#15803d;">
|
||||||
|
Tu turno
|
||||||
|
</p>
|
||||||
|
<p style="margin:12px 0 0;font-size:28px;font-weight:900;color:#111827;line-height:1.1;letter-spacing:-0.025em;">
|
||||||
|
${friendlyDate}
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:24px;font-weight:900;color:#059669;line-height:1;letter-spacing:-0.025em;">
|
||||||
|
${data.startTime} — ${data.endTime}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background-color:#fffbeb;border-radius:24px;border:1px solid rgba(217,119,6,0.3);">
|
||||||
|
<tr>
|
||||||
|
<td style="padding:20px 24px;">
|
||||||
|
<p style="margin:0;font-size:14px;color:#374151;line-height:1.6;">
|
||||||
|
La reserva <strong style="font-family:monospace;font-weight:700;letter-spacing:2px;">${data.bookingCode}</strong>
|
||||||
|
fue registrada como no concretada por falta de asistencia.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 32px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border:1px solid #e5e7eb;border-radius:22px;overflow:hidden;">
|
||||||
|
<tr>
|
||||||
|
<td width="50%" style="padding:16px;border-right:1px solid #e5e7eb;background-color:#ffffff;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||||
|
Cancha
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||||
|
${data.courtName} — ${data.sportName}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
<td width="50%" style="padding:16px;background-color:#ffffff;">
|
||||||
|
<p style="margin:0;font-size:13px;font-weight:500;color:#6b7280;">
|
||||||
|
Cliente
|
||||||
|
</p>
|
||||||
|
<p style="margin:8px 0 0;font-size:14px;font-weight:600;color:#111827;">
|
||||||
|
${data.customerName}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
return wrapLayout(content);
|
||||||
|
}
|
||||||
@@ -17,6 +17,26 @@ import * as Prisma from './internal/prismaNamespaceBrowser'
|
|||||||
export { Prisma }
|
export { Prisma }
|
||||||
export * as $Enums from './enums'
|
export * as $Enums from './enums'
|
||||||
export * from './enums';
|
export * from './enums';
|
||||||
|
/**
|
||||||
|
* Model User
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type User = Prisma.UserModel
|
||||||
|
/**
|
||||||
|
* Model Session
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Session = Prisma.SessionModel
|
||||||
|
/**
|
||||||
|
* Model Account
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Account = Prisma.AccountModel
|
||||||
|
/**
|
||||||
|
* Model Verification
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Verification = Prisma.VerificationModel
|
||||||
/**
|
/**
|
||||||
* Model Complex
|
* Model Complex
|
||||||
*
|
*
|
||||||
@@ -27,6 +47,11 @@ export type Complex = Prisma.ComplexModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ComplexUser = Prisma.ComplexUserModel
|
export type ComplexUser = Prisma.ComplexUserModel
|
||||||
|
/**
|
||||||
|
* Model ComplexInvitation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ComplexInvitation = Prisma.ComplexInvitationModel
|
||||||
/**
|
/**
|
||||||
* Model Sport
|
* Model Sport
|
||||||
*
|
*
|
||||||
@@ -53,17 +78,17 @@ export type CourtPriceRule = Prisma.CourtPriceRuleModel
|
|||||||
*/
|
*/
|
||||||
export type CourtBooking = Prisma.CourtBookingModel
|
export type CourtBooking = Prisma.CourtBookingModel
|
||||||
/**
|
/**
|
||||||
* Model OnboardingRequest
|
* Model CourtBookingLog
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type OnboardingRequest = Prisma.OnboardingRequestModel
|
export type CourtBookingLog = Prisma.CourtBookingLogModel
|
||||||
|
/**
|
||||||
|
* Model PasswordResetRequest
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type PasswordResetRequest = Prisma.PasswordResetRequestModel
|
||||||
/**
|
/**
|
||||||
* Model Plan
|
* Model Plan
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Plan = Prisma.PlanModel
|
export type Plan = Prisma.PlanModel
|
||||||
/**
|
|
||||||
* Model User
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type User = Prisma.UserModel
|
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ export * from "./enums"
|
|||||||
* const prisma = new PrismaClient({
|
* const prisma = new PrismaClient({
|
||||||
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
* adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL })
|
||||||
* })
|
* })
|
||||||
* // Fetch zero or more Complexes
|
* // Fetch zero or more Users
|
||||||
* const complexes = await prisma.complex.findMany()
|
* const users = await prisma.user.findMany()
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Read more in our [docs](https://pris.ly/d/client).
|
* Read more in our [docs](https://pris.ly/d/client).
|
||||||
@@ -41,6 +41,26 @@ export const PrismaClient = $Class.getPrismaClientClass()
|
|||||||
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
export type PrismaClient<LogOpts extends Prisma.LogLevel = never, OmitOpts extends Prisma.PrismaClientOptions["omit"] = Prisma.PrismaClientOptions["omit"], ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = $Class.PrismaClient<LogOpts, OmitOpts, ExtArgs>
|
||||||
export { Prisma }
|
export { Prisma }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model User
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type User = Prisma.UserModel
|
||||||
|
/**
|
||||||
|
* Model Session
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Session = Prisma.SessionModel
|
||||||
|
/**
|
||||||
|
* Model Account
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Account = Prisma.AccountModel
|
||||||
|
/**
|
||||||
|
* Model Verification
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type Verification = Prisma.VerificationModel
|
||||||
/**
|
/**
|
||||||
* Model Complex
|
* Model Complex
|
||||||
*
|
*
|
||||||
@@ -51,6 +71,11 @@ export type Complex = Prisma.ComplexModel
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type ComplexUser = Prisma.ComplexUserModel
|
export type ComplexUser = Prisma.ComplexUserModel
|
||||||
|
/**
|
||||||
|
* Model ComplexInvitation
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type ComplexInvitation = Prisma.ComplexInvitationModel
|
||||||
/**
|
/**
|
||||||
* Model Sport
|
* Model Sport
|
||||||
*
|
*
|
||||||
@@ -77,17 +102,17 @@ export type CourtPriceRule = Prisma.CourtPriceRuleModel
|
|||||||
*/
|
*/
|
||||||
export type CourtBooking = Prisma.CourtBookingModel
|
export type CourtBooking = Prisma.CourtBookingModel
|
||||||
/**
|
/**
|
||||||
* Model OnboardingRequest
|
* Model CourtBookingLog
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type OnboardingRequest = Prisma.OnboardingRequestModel
|
export type CourtBookingLog = Prisma.CourtBookingLogModel
|
||||||
|
/**
|
||||||
|
* Model PasswordResetRequest
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export type PasswordResetRequest = Prisma.PasswordResetRequestModel
|
||||||
/**
|
/**
|
||||||
* Model Plan
|
* Model Plan
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export type Plan = Prisma.PlanModel
|
export type Plan = Prisma.PlanModel
|
||||||
/**
|
|
||||||
* Model User
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export type User = Prisma.UserModel
|
|
||||||
|
|||||||
@@ -14,18 +14,6 @@ import * as $Enums from "./enums"
|
|||||||
import type * as Prisma from "./internal/prismaNamespace"
|
import type * as Prisma from "./internal/prismaNamespace"
|
||||||
|
|
||||||
|
|
||||||
export type UuidFilter<$PrismaModel = never> = {
|
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
mode?: Prisma.QueryMode
|
|
||||||
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type StringFilter<$PrismaModel = never> = {
|
export type StringFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -41,6 +29,11 @@ export type StringFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BoolFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type StringNullableFilter<$PrismaModel = never> = {
|
export type StringNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
@@ -72,21 +65,6 @@ export type SortOrderInput = {
|
|||||||
nulls?: Prisma.NullsOrder
|
nulls?: Prisma.NullsOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UuidWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
mode?: Prisma.QueryMode
|
|
||||||
not?: Prisma.NestedUuidWithAggregatesFilter<$PrismaModel> | string
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -105,6 +83,14 @@ export type StringWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
export type StringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
@@ -137,6 +123,58 @@ export type DateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type DateTimeNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UuidFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
mode?: Prisma.QueryMode
|
||||||
|
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UuidWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
mode?: Prisma.QueryMode
|
||||||
|
not?: Prisma.NestedUuidWithAggregatesFilter<$PrismaModel> | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type EnumComplexUserRoleFilter<$PrismaModel = never> = {
|
export type EnumComplexUserRoleFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.ComplexUserRole | Prisma.EnumComplexUserRoleFieldRefInput<$PrismaModel>
|
equals?: $Enums.ComplexUserRole | Prisma.EnumComplexUserRoleFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.ComplexUserRole[] | Prisma.ListEnumComplexUserRoleFieldRefInput<$PrismaModel>
|
in?: $Enums.ComplexUserRole[] | Prisma.ListEnumComplexUserRoleFieldRefInput<$PrismaModel>
|
||||||
@@ -154,19 +192,6 @@ export type EnumComplexUserRoleWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BoolFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type BoolWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type IntFilter<$PrismaModel = never> = {
|
export type IntFilter<$PrismaModel = never> = {
|
||||||
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
equals?: number | Prisma.IntFieldRefInput<$PrismaModel>
|
||||||
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
in?: number[] | Prisma.ListIntFieldRefInput<$PrismaModel>
|
||||||
@@ -272,31 +297,6 @@ export type EnumCourtBookingStatusWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel>
|
_max?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DateTimeNullableFilter<$PrismaModel = never> = {
|
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type JsonFilter<$PrismaModel = never> =
|
export type JsonFilter<$PrismaModel = never> =
|
||||||
| Prisma.PatchUndefined<
|
| Prisma.PatchUndefined<
|
||||||
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
|
Prisma.Either<Required<JsonFilterBase<$PrismaModel>>, Exclude<keyof Required<JsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
@@ -348,17 +348,6 @@ export type JsonWithAggregatesFilterBase<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
_max?: Prisma.NestedJsonFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedUuidFilter<$PrismaModel = never> = {
|
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedStringFilter<$PrismaModel = never> = {
|
export type NestedStringFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -373,6 +362,11 @@ export type NestedStringFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
not?: Prisma.NestedStringFilter<$PrismaModel> | string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedBoolFilter<$PrismaModel = never> = {
|
||||||
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
export type NestedStringNullableFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel> | null
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel> | null
|
||||||
@@ -398,7 +392,7 @@ export type NestedDateTimeFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
not?: Prisma.NestedDateTimeFilter<$PrismaModel> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedUuidWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
@@ -406,7 +400,10 @@ export type NestedUuidWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
not?: Prisma.NestedUuidWithAggregatesFilter<$PrismaModel> | string
|
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
@@ -423,21 +420,12 @@ export type NestedIntFilter<$PrismaModel = never> = {
|
|||||||
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
not?: Prisma.NestedIntFilter<$PrismaModel> | number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedStringWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
||||||
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
||||||
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
|
||||||
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
contains?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
startsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
endsWith?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedStringWithAggregatesFilter<$PrismaModel> | string
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
export type NestedStringNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
@@ -482,6 +470,56 @@ export type NestedDateTimeWithAggregatesFilter<$PrismaModel = never> = {
|
|||||||
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
_max?: Prisma.NestedDateTimeFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
||||||
|
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
||||||
|
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedUuidFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedUuidFilter<$PrismaModel> | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type NestedUuidWithAggregatesFilter<$PrismaModel = never> = {
|
||||||
|
equals?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
in?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
notIn?: string[] | Prisma.ListStringFieldRefInput<$PrismaModel>
|
||||||
|
lt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
lte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gt?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
gte?: string | Prisma.StringFieldRefInput<$PrismaModel>
|
||||||
|
not?: Prisma.NestedUuidWithAggregatesFilter<$PrismaModel> | string
|
||||||
|
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
||||||
|
_min?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
_max?: Prisma.NestedStringFilter<$PrismaModel>
|
||||||
|
}
|
||||||
|
|
||||||
export type NestedEnumComplexUserRoleFilter<$PrismaModel = never> = {
|
export type NestedEnumComplexUserRoleFilter<$PrismaModel = never> = {
|
||||||
equals?: $Enums.ComplexUserRole | Prisma.EnumComplexUserRoleFieldRefInput<$PrismaModel>
|
equals?: $Enums.ComplexUserRole | Prisma.EnumComplexUserRoleFieldRefInput<$PrismaModel>
|
||||||
in?: $Enums.ComplexUserRole[] | Prisma.ListEnumComplexUserRoleFieldRefInput<$PrismaModel>
|
in?: $Enums.ComplexUserRole[] | Prisma.ListEnumComplexUserRoleFieldRefInput<$PrismaModel>
|
||||||
@@ -499,19 +537,6 @@ export type NestedEnumComplexUserRoleWithAggregatesFilter<$PrismaModel = never>
|
|||||||
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
_max?: Prisma.NestedEnumComplexUserRoleFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedBoolFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolFilter<$PrismaModel> | boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedBoolWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: boolean | Prisma.BooleanFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedBoolWithAggregatesFilter<$PrismaModel> | boolean
|
|
||||||
_count?: Prisma.NestedIntFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedBoolFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedDecimalFilter<$PrismaModel = never> = {
|
export type NestedDecimalFilter<$PrismaModel = never> = {
|
||||||
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
equals?: runtime.Decimal | runtime.DecimalJsLike | number | string | Prisma.DecimalFieldRefInput<$PrismaModel>
|
||||||
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
in?: runtime.Decimal[] | runtime.DecimalJsLike[] | number[] | string[] | Prisma.ListDecimalFieldRefInput<$PrismaModel>
|
||||||
@@ -617,31 +642,6 @@ export type NestedEnumCourtBookingStatusWithAggregatesFilter<$PrismaModel = neve
|
|||||||
_max?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel>
|
_max?: Prisma.NestedEnumCourtBookingStatusFilter<$PrismaModel>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NestedDateTimeNullableFilter<$PrismaModel = never> = {
|
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeNullableFilter<$PrismaModel> | Date | string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedDateTimeNullableWithAggregatesFilter<$PrismaModel = never> = {
|
|
||||||
equals?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
in?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
notIn?: Date[] | string[] | Prisma.ListDateTimeFieldRefInput<$PrismaModel> | null
|
|
||||||
lt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
lte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gt?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
gte?: Date | string | Prisma.DateTimeFieldRefInput<$PrismaModel>
|
|
||||||
not?: Prisma.NestedDateTimeNullableWithAggregatesFilter<$PrismaModel> | Date | string | null
|
|
||||||
_count?: Prisma.NestedIntNullableFilter<$PrismaModel>
|
|
||||||
_min?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
_max?: Prisma.NestedDateTimeNullableFilter<$PrismaModel>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NestedJsonFilter<$PrismaModel = never> =
|
export type NestedJsonFilter<$PrismaModel = never> =
|
||||||
| Prisma.PatchUndefined<
|
| Prisma.PatchUndefined<
|
||||||
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
|
Prisma.Either<Required<NestedJsonFilterBase<$PrismaModel>>, Exclude<keyof Required<NestedJsonFilterBase<$PrismaModel>>, 'path'>>,
|
||||||
|
|||||||
@@ -10,7 +10,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export const ComplexUserRole = {
|
export const ComplexUserRole = {
|
||||||
ADMIN: 'ADMIN'
|
ADMIN: 'ADMIN',
|
||||||
|
EMPLOYEE: 'EMPLOYEE'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ComplexUserRole = (typeof ComplexUserRole)[keyof typeof ComplexUserRole]
|
export type ComplexUserRole = (typeof ComplexUserRole)[keyof typeof ComplexUserRole]
|
||||||
@@ -31,7 +32,9 @@ export type DayOfWeek = (typeof DayOfWeek)[keyof typeof DayOfWeek]
|
|||||||
|
|
||||||
export const CourtBookingStatus = {
|
export const CourtBookingStatus = {
|
||||||
CONFIRMED: 'CONFIRMED',
|
CONFIRMED: 'CONFIRMED',
|
||||||
CANCELLED: 'CANCELLED'
|
CANCELLED: 'CANCELLED',
|
||||||
|
COMPLETED: 'COMPLETED',
|
||||||
|
NOSHOW: 'NOSHOW'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type CourtBookingStatus = (typeof CourtBookingStatus)[keyof typeof CourtBookingStatus]
|
export type CourtBookingStatus = (typeof CourtBookingStatus)[keyof typeof CourtBookingStatus]
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -384,16 +384,21 @@ type FieldRefInputType<Model, FieldType> = Model extends never ? never : FieldRe
|
|||||||
|
|
||||||
|
|
||||||
export const ModelName = {
|
export const ModelName = {
|
||||||
|
User: 'User',
|
||||||
|
Session: 'Session',
|
||||||
|
Account: 'Account',
|
||||||
|
Verification: 'Verification',
|
||||||
Complex: 'Complex',
|
Complex: 'Complex',
|
||||||
ComplexUser: 'ComplexUser',
|
ComplexUser: 'ComplexUser',
|
||||||
|
ComplexInvitation: 'ComplexInvitation',
|
||||||
Sport: 'Sport',
|
Sport: 'Sport',
|
||||||
Court: 'Court',
|
Court: 'Court',
|
||||||
CourtAvailability: 'CourtAvailability',
|
CourtAvailability: 'CourtAvailability',
|
||||||
CourtPriceRule: 'CourtPriceRule',
|
CourtPriceRule: 'CourtPriceRule',
|
||||||
CourtBooking: 'CourtBooking',
|
CourtBooking: 'CourtBooking',
|
||||||
OnboardingRequest: 'OnboardingRequest',
|
CourtBookingLog: 'CourtBookingLog',
|
||||||
Plan: 'Plan',
|
PasswordResetRequest: 'PasswordResetRequest',
|
||||||
User: 'User'
|
Plan: 'Plan'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
@@ -409,10 +414,306 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
omit: GlobalOmitOptions
|
omit: GlobalOmitOptions
|
||||||
}
|
}
|
||||||
meta: {
|
meta: {
|
||||||
modelProps: "complex" | "complexUser" | "sport" | "court" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "onboardingRequest" | "plan" | "user"
|
modelProps: "user" | "session" | "account" | "verification" | "complex" | "complexUser" | "complexInvitation" | "sport" | "court" | "courtAvailability" | "courtPriceRule" | "courtBooking" | "courtBookingLog" | "passwordResetRequest" | "plan"
|
||||||
txIsolationLevel: TransactionIsolationLevel
|
txIsolationLevel: TransactionIsolationLevel
|
||||||
}
|
}
|
||||||
model: {
|
model: {
|
||||||
|
User: {
|
||||||
|
payload: Prisma.$UserPayload<ExtArgs>
|
||||||
|
fields: Prisma.UserFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.UserFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.UserFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.UserFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.UserFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.UserFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.UserCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.UserCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.UserCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.UserDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.UserUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.UserDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.UserUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.UserUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.UserUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.UserAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateUser>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.UserGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.UserGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.UserCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.UserCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Session: {
|
||||||
|
payload: Prisma.$SessionPayload<ExtArgs>
|
||||||
|
fields: Prisma.SessionFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.SessionFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.SessionFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.SessionFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.SessionFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.SessionFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.SessionCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.SessionCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.SessionCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.SessionDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.SessionUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.SessionDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.SessionUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.SessionUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.SessionUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$SessionPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.SessionAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateSession>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.SessionGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.SessionGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.SessionCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.SessionCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Account: {
|
||||||
|
payload: Prisma.$AccountPayload<ExtArgs>
|
||||||
|
fields: Prisma.AccountFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.AccountFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.AccountFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.AccountFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.AccountFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.AccountFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.AccountCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.AccountCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.AccountCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.AccountDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.AccountUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.AccountDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.AccountUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.AccountUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.AccountUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$AccountPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.AccountAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateAccount>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.AccountGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AccountGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.AccountCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AccountCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Verification: {
|
||||||
|
payload: Prisma.$VerificationPayload<ExtArgs>
|
||||||
|
fields: Prisma.VerificationFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.VerificationFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.VerificationFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.VerificationFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.VerificationFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.VerificationFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.VerificationCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.VerificationCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.VerificationCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.VerificationDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.VerificationUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.VerificationDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.VerificationUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.VerificationUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.VerificationUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$VerificationPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.VerificationAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateVerification>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.VerificationGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.VerificationGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.VerificationCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.VerificationCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Complex: {
|
Complex: {
|
||||||
payload: Prisma.$ComplexPayload<ExtArgs>
|
payload: Prisma.$ComplexPayload<ExtArgs>
|
||||||
fields: Prisma.ComplexFieldRefs
|
fields: Prisma.ComplexFieldRefs
|
||||||
@@ -561,6 +862,80 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ComplexInvitation: {
|
||||||
|
payload: Prisma.$ComplexInvitationPayload<ExtArgs>
|
||||||
|
fields: Prisma.ComplexInvitationFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.ComplexInvitationFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.ComplexInvitationFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.ComplexInvitationFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.ComplexInvitationFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.ComplexInvitationFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.ComplexInvitationCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.ComplexInvitationCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.ComplexInvitationCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.ComplexInvitationDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.ComplexInvitationUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.ComplexInvitationDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.ComplexInvitationUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.ComplexInvitationUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.ComplexInvitationUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$ComplexInvitationPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.ComplexInvitationAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregateComplexInvitation>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.ComplexInvitationGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ComplexInvitationGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.ComplexInvitationCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.ComplexInvitationCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Sport: {
|
Sport: {
|
||||||
payload: Prisma.$SportPayload<ExtArgs>
|
payload: Prisma.$SportPayload<ExtArgs>
|
||||||
fields: Prisma.SportFieldRefs
|
fields: Prisma.SportFieldRefs
|
||||||
@@ -931,77 +1306,151 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
OnboardingRequest: {
|
CourtBookingLog: {
|
||||||
payload: Prisma.$OnboardingRequestPayload<ExtArgs>
|
payload: Prisma.$CourtBookingLogPayload<ExtArgs>
|
||||||
fields: Prisma.OnboardingRequestFieldRefs
|
fields: Prisma.CourtBookingLogFieldRefs
|
||||||
operations: {
|
operations: {
|
||||||
findUnique: {
|
findUnique: {
|
||||||
args: Prisma.OnboardingRequestFindUniqueArgs<ExtArgs>
|
args: Prisma.CourtBookingLogFindUniqueArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload> | null
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload> | null
|
||||||
}
|
}
|
||||||
findUniqueOrThrow: {
|
findUniqueOrThrow: {
|
||||||
args: Prisma.OnboardingRequestFindUniqueOrThrowArgs<ExtArgs>
|
args: Prisma.CourtBookingLogFindUniqueOrThrowArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>
|
||||||
}
|
}
|
||||||
findFirst: {
|
findFirst: {
|
||||||
args: Prisma.OnboardingRequestFindFirstArgs<ExtArgs>
|
args: Prisma.CourtBookingLogFindFirstArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload> | null
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload> | null
|
||||||
}
|
}
|
||||||
findFirstOrThrow: {
|
findFirstOrThrow: {
|
||||||
args: Prisma.OnboardingRequestFindFirstOrThrowArgs<ExtArgs>
|
args: Prisma.CourtBookingLogFindFirstOrThrowArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>
|
||||||
}
|
}
|
||||||
findMany: {
|
findMany: {
|
||||||
args: Prisma.OnboardingRequestFindManyArgs<ExtArgs>
|
args: Prisma.CourtBookingLogFindManyArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>[]
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>[]
|
||||||
}
|
}
|
||||||
create: {
|
create: {
|
||||||
args: Prisma.OnboardingRequestCreateArgs<ExtArgs>
|
args: Prisma.CourtBookingLogCreateArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>
|
||||||
}
|
}
|
||||||
createMany: {
|
createMany: {
|
||||||
args: Prisma.OnboardingRequestCreateManyArgs<ExtArgs>
|
args: Prisma.CourtBookingLogCreateManyArgs<ExtArgs>
|
||||||
result: BatchPayload
|
result: BatchPayload
|
||||||
}
|
}
|
||||||
createManyAndReturn: {
|
createManyAndReturn: {
|
||||||
args: Prisma.OnboardingRequestCreateManyAndReturnArgs<ExtArgs>
|
args: Prisma.CourtBookingLogCreateManyAndReturnArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>[]
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>[]
|
||||||
}
|
}
|
||||||
delete: {
|
delete: {
|
||||||
args: Prisma.OnboardingRequestDeleteArgs<ExtArgs>
|
args: Prisma.CourtBookingLogDeleteArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>
|
||||||
}
|
}
|
||||||
update: {
|
update: {
|
||||||
args: Prisma.OnboardingRequestUpdateArgs<ExtArgs>
|
args: Prisma.CourtBookingLogUpdateArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>
|
||||||
}
|
}
|
||||||
deleteMany: {
|
deleteMany: {
|
||||||
args: Prisma.OnboardingRequestDeleteManyArgs<ExtArgs>
|
args: Prisma.CourtBookingLogDeleteManyArgs<ExtArgs>
|
||||||
result: BatchPayload
|
result: BatchPayload
|
||||||
}
|
}
|
||||||
updateMany: {
|
updateMany: {
|
||||||
args: Prisma.OnboardingRequestUpdateManyArgs<ExtArgs>
|
args: Prisma.CourtBookingLogUpdateManyArgs<ExtArgs>
|
||||||
result: BatchPayload
|
result: BatchPayload
|
||||||
}
|
}
|
||||||
updateManyAndReturn: {
|
updateManyAndReturn: {
|
||||||
args: Prisma.OnboardingRequestUpdateManyAndReturnArgs<ExtArgs>
|
args: Prisma.CourtBookingLogUpdateManyAndReturnArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>[]
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>[]
|
||||||
}
|
}
|
||||||
upsert: {
|
upsert: {
|
||||||
args: Prisma.OnboardingRequestUpsertArgs<ExtArgs>
|
args: Prisma.CourtBookingLogUpsertArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$OnboardingRequestPayload>
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$CourtBookingLogPayload>
|
||||||
}
|
}
|
||||||
aggregate: {
|
aggregate: {
|
||||||
args: Prisma.OnboardingRequestAggregateArgs<ExtArgs>
|
args: Prisma.CourtBookingLogAggregateArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.Optional<Prisma.AggregateOnboardingRequest>
|
result: runtime.Types.Utils.Optional<Prisma.AggregateCourtBookingLog>
|
||||||
}
|
}
|
||||||
groupBy: {
|
groupBy: {
|
||||||
args: Prisma.OnboardingRequestGroupByArgs<ExtArgs>
|
args: Prisma.CourtBookingLogGroupByArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.Optional<Prisma.OnboardingRequestGroupByOutputType>[]
|
result: runtime.Types.Utils.Optional<Prisma.CourtBookingLogGroupByOutputType>[]
|
||||||
}
|
}
|
||||||
count: {
|
count: {
|
||||||
args: Prisma.OnboardingRequestCountArgs<ExtArgs>
|
args: Prisma.CourtBookingLogCountArgs<ExtArgs>
|
||||||
result: runtime.Types.Utils.Optional<Prisma.OnboardingRequestCountAggregateOutputType> | number
|
result: runtime.Types.Utils.Optional<Prisma.CourtBookingLogCountAggregateOutputType> | number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PasswordResetRequest: {
|
||||||
|
payload: Prisma.$PasswordResetRequestPayload<ExtArgs>
|
||||||
|
fields: Prisma.PasswordResetRequestFieldRefs
|
||||||
|
operations: {
|
||||||
|
findUnique: {
|
||||||
|
args: Prisma.PasswordResetRequestFindUniqueArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload> | null
|
||||||
|
}
|
||||||
|
findUniqueOrThrow: {
|
||||||
|
args: Prisma.PasswordResetRequestFindUniqueOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
|
}
|
||||||
|
findFirst: {
|
||||||
|
args: Prisma.PasswordResetRequestFindFirstArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload> | null
|
||||||
|
}
|
||||||
|
findFirstOrThrow: {
|
||||||
|
args: Prisma.PasswordResetRequestFindFirstOrThrowArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
|
}
|
||||||
|
findMany: {
|
||||||
|
args: Prisma.PasswordResetRequestFindManyArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>[]
|
||||||
|
}
|
||||||
|
create: {
|
||||||
|
args: Prisma.PasswordResetRequestCreateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
|
}
|
||||||
|
createMany: {
|
||||||
|
args: Prisma.PasswordResetRequestCreateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
createManyAndReturn: {
|
||||||
|
args: Prisma.PasswordResetRequestCreateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>[]
|
||||||
|
}
|
||||||
|
delete: {
|
||||||
|
args: Prisma.PasswordResetRequestDeleteArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
|
}
|
||||||
|
update: {
|
||||||
|
args: Prisma.PasswordResetRequestUpdateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
|
}
|
||||||
|
deleteMany: {
|
||||||
|
args: Prisma.PasswordResetRequestDeleteManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateMany: {
|
||||||
|
args: Prisma.PasswordResetRequestUpdateManyArgs<ExtArgs>
|
||||||
|
result: BatchPayload
|
||||||
|
}
|
||||||
|
updateManyAndReturn: {
|
||||||
|
args: Prisma.PasswordResetRequestUpdateManyAndReturnArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>[]
|
||||||
|
}
|
||||||
|
upsert: {
|
||||||
|
args: Prisma.PasswordResetRequestUpsertArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.PayloadToResult<Prisma.$PasswordResetRequestPayload>
|
||||||
|
}
|
||||||
|
aggregate: {
|
||||||
|
args: Prisma.PasswordResetRequestAggregateArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.AggregatePasswordResetRequest>
|
||||||
|
}
|
||||||
|
groupBy: {
|
||||||
|
args: Prisma.PasswordResetRequestGroupByArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.PasswordResetRequestGroupByOutputType>[]
|
||||||
|
}
|
||||||
|
count: {
|
||||||
|
args: Prisma.PasswordResetRequestCountArgs<ExtArgs>
|
||||||
|
result: runtime.Types.Utils.Optional<Prisma.PasswordResetRequestCountAggregateOutputType> | number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1079,80 +1528,6 @@ export type TypeMap<ExtArgs extends runtime.Types.Extensions.InternalArgs = runt
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
User: {
|
|
||||||
payload: Prisma.$UserPayload<ExtArgs>
|
|
||||||
fields: Prisma.UserFieldRefs
|
|
||||||
operations: {
|
|
||||||
findUnique: {
|
|
||||||
args: Prisma.UserFindUniqueArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
|
|
||||||
}
|
|
||||||
findUniqueOrThrow: {
|
|
||||||
args: Prisma.UserFindUniqueOrThrowArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
|
||||||
}
|
|
||||||
findFirst: {
|
|
||||||
args: Prisma.UserFindFirstArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload> | null
|
|
||||||
}
|
|
||||||
findFirstOrThrow: {
|
|
||||||
args: Prisma.UserFindFirstOrThrowArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
|
||||||
}
|
|
||||||
findMany: {
|
|
||||||
args: Prisma.UserFindManyArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
|
||||||
}
|
|
||||||
create: {
|
|
||||||
args: Prisma.UserCreateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
|
||||||
}
|
|
||||||
createMany: {
|
|
||||||
args: Prisma.UserCreateManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
createManyAndReturn: {
|
|
||||||
args: Prisma.UserCreateManyAndReturnArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
|
||||||
}
|
|
||||||
delete: {
|
|
||||||
args: Prisma.UserDeleteArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
|
||||||
}
|
|
||||||
update: {
|
|
||||||
args: Prisma.UserUpdateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
|
||||||
}
|
|
||||||
deleteMany: {
|
|
||||||
args: Prisma.UserDeleteManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
updateMany: {
|
|
||||||
args: Prisma.UserUpdateManyArgs<ExtArgs>
|
|
||||||
result: BatchPayload
|
|
||||||
}
|
|
||||||
updateManyAndReturn: {
|
|
||||||
args: Prisma.UserUpdateManyAndReturnArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>[]
|
|
||||||
}
|
|
||||||
upsert: {
|
|
||||||
args: Prisma.UserUpsertArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.PayloadToResult<Prisma.$UserPayload>
|
|
||||||
}
|
|
||||||
aggregate: {
|
|
||||||
args: Prisma.UserAggregateArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.AggregateUser>
|
|
||||||
}
|
|
||||||
groupBy: {
|
|
||||||
args: Prisma.UserGroupByArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.UserGroupByOutputType>[]
|
|
||||||
}
|
|
||||||
count: {
|
|
||||||
args: Prisma.UserCountArgs<ExtArgs>
|
|
||||||
result: runtime.Types.Utils.Optional<Prisma.UserCountAggregateOutputType> | number
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} & {
|
} & {
|
||||||
other: {
|
other: {
|
||||||
@@ -1192,10 +1567,73 @@ export const TransactionIsolationLevel = runtime.makeStrictEnum({
|
|||||||
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||||
|
|
||||||
|
|
||||||
|
export const UserScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
email: 'email',
|
||||||
|
emailVerified: 'emailVerified',
|
||||||
|
image: 'image',
|
||||||
|
phone: 'phone',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
role: 'role'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const SessionScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
token: 'token',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
ipAddress: 'ipAddress',
|
||||||
|
userAgent: 'userAgent',
|
||||||
|
userId: 'userId'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SessionScalarFieldEnum = (typeof SessionScalarFieldEnum)[keyof typeof SessionScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const AccountScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
accountId: 'accountId',
|
||||||
|
providerId: 'providerId',
|
||||||
|
userId: 'userId',
|
||||||
|
accessToken: 'accessToken',
|
||||||
|
refreshToken: 'refreshToken',
|
||||||
|
idToken: 'idToken',
|
||||||
|
accessTokenExpiresAt: 'accessTokenExpiresAt',
|
||||||
|
refreshTokenExpiresAt: 'refreshTokenExpiresAt',
|
||||||
|
scope: 'scope',
|
||||||
|
password: 'password',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type AccountScalarFieldEnum = (typeof AccountScalarFieldEnum)[keyof typeof AccountScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const VerificationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
identifier: 'identifier',
|
||||||
|
value: 'value',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type VerificationScalarFieldEnum = (typeof VerificationScalarFieldEnum)[keyof typeof VerificationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const ComplexScalarFieldEnum = {
|
export const ComplexScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
complexName: 'complexName',
|
complexName: 'complexName',
|
||||||
physicalAddress: 'physicalAddress',
|
physicalAddress: 'physicalAddress',
|
||||||
|
city: 'city',
|
||||||
|
state: 'state',
|
||||||
|
country: 'country',
|
||||||
complexSlug: 'complexSlug',
|
complexSlug: 'complexSlug',
|
||||||
adminEmail: 'adminEmail',
|
adminEmail: 'adminEmail',
|
||||||
planCode: 'planCode',
|
planCode: 'planCode',
|
||||||
@@ -1216,6 +1654,21 @@ export const ComplexUserScalarFieldEnum = {
|
|||||||
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ComplexInvitationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
complexId: 'complexId',
|
||||||
|
email: 'email',
|
||||||
|
tokenHash: 'tokenHash',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
acceptedAt: 'acceptedAt',
|
||||||
|
revokedAt: 'revokedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ComplexInvitationScalarFieldEnum = (typeof ComplexInvitationScalarFieldEnum)[keyof typeof ComplexInvitationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SportScalarFieldEnum = {
|
export const SportScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
@@ -1278,6 +1731,7 @@ export const CourtBookingScalarFieldEnum = {
|
|||||||
endTime: 'endTime',
|
endTime: 'endTime',
|
||||||
customerName: 'customerName',
|
customerName: 'customerName',
|
||||||
customerPhone: 'customerPhone',
|
customerPhone: 'customerPhone',
|
||||||
|
customerEmail: 'customerEmail',
|
||||||
status: 'status',
|
status: 'status',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
@@ -1286,22 +1740,38 @@ export const CourtBookingScalarFieldEnum = {
|
|||||||
export type CourtBookingScalarFieldEnum = (typeof CourtBookingScalarFieldEnum)[keyof typeof CourtBookingScalarFieldEnum]
|
export type CourtBookingScalarFieldEnum = (typeof CourtBookingScalarFieldEnum)[keyof typeof CourtBookingScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const OnboardingRequestScalarFieldEnum = {
|
export const CourtBookingLogScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
bookingCode: 'bookingCode',
|
||||||
|
courtId: 'courtId',
|
||||||
|
bookingDate: 'bookingDate',
|
||||||
|
startTime: 'startTime',
|
||||||
|
endTime: 'endTime',
|
||||||
|
previousStatus: 'previousStatus',
|
||||||
|
newStatus: 'newStatus',
|
||||||
|
customerName: 'customerName',
|
||||||
|
customerPhone: 'customerPhone',
|
||||||
|
customerEmail: 'customerEmail',
|
||||||
|
changedAt: 'changedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type CourtBookingLogScalarFieldEnum = (typeof CourtBookingLogScalarFieldEnum)[keyof typeof CourtBookingLogScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const PasswordResetRequestScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
fullName: 'fullName',
|
|
||||||
email: 'email',
|
email: 'email',
|
||||||
otpHash: 'otpHash',
|
otpHash: 'otpHash',
|
||||||
otpExpiresAt: 'otpExpiresAt',
|
otpExpiresAt: 'otpExpiresAt',
|
||||||
otpAttempts: 'otpAttempts',
|
otpAttempts: 'otpAttempts',
|
||||||
otpLastSentAt: 'otpLastSentAt',
|
otpLastSentAt: 'otpLastSentAt',
|
||||||
otpResendCount: 'otpResendCount',
|
otpResendCount: 'otpResendCount',
|
||||||
emailVerifiedAt: 'emailVerifiedAt',
|
usedAt: 'usedAt',
|
||||||
completedAt: 'completedAt',
|
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type OnboardingRequestScalarFieldEnum = (typeof OnboardingRequestScalarFieldEnum)[keyof typeof OnboardingRequestScalarFieldEnum]
|
export type PasswordResetRequestScalarFieldEnum = (typeof PasswordResetRequestScalarFieldEnum)[keyof typeof PasswordResetRequestScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const PlanScalarFieldEnum = {
|
export const PlanScalarFieldEnum = {
|
||||||
@@ -1315,18 +1785,6 @@ export const PlanScalarFieldEnum = {
|
|||||||
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const UserScalarFieldEnum = {
|
|
||||||
id: 'id',
|
|
||||||
supabaseUserId: 'supabaseUserId',
|
|
||||||
email: 'email',
|
|
||||||
fullName: 'fullName',
|
|
||||||
createdAt: 'createdAt',
|
|
||||||
updatedAt: 'updatedAt'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
|
||||||
|
|
||||||
|
|
||||||
export const SortOrder = {
|
export const SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
@@ -1387,6 +1845,13 @@ export type ListStringFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaMod
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reference to a field of type 'Boolean'
|
||||||
|
*/
|
||||||
|
export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'DateTime'
|
* Reference to a field of type 'DateTime'
|
||||||
*/
|
*/
|
||||||
@@ -1415,13 +1880,6 @@ export type ListEnumComplexUserRoleFieldRefInput<$PrismaModel> = FieldRefInputTy
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to a field of type 'Boolean'
|
|
||||||
*/
|
|
||||||
export type BooleanFieldRefInput<$PrismaModel> = FieldRefInputType<$PrismaModel, 'Boolean'>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reference to a field of type 'Int'
|
* Reference to a field of type 'Int'
|
||||||
*/
|
*/
|
||||||
@@ -1600,16 +2058,21 @@ export type PrismaClientOptions = ({
|
|||||||
comments?: runtime.SqlCommenterPlugin[]
|
comments?: runtime.SqlCommenterPlugin[]
|
||||||
}
|
}
|
||||||
export type GlobalOmitConfig = {
|
export type GlobalOmitConfig = {
|
||||||
|
user?: Prisma.UserOmit
|
||||||
|
session?: Prisma.SessionOmit
|
||||||
|
account?: Prisma.AccountOmit
|
||||||
|
verification?: Prisma.VerificationOmit
|
||||||
complex?: Prisma.ComplexOmit
|
complex?: Prisma.ComplexOmit
|
||||||
complexUser?: Prisma.ComplexUserOmit
|
complexUser?: Prisma.ComplexUserOmit
|
||||||
|
complexInvitation?: Prisma.ComplexInvitationOmit
|
||||||
sport?: Prisma.SportOmit
|
sport?: Prisma.SportOmit
|
||||||
court?: Prisma.CourtOmit
|
court?: Prisma.CourtOmit
|
||||||
courtAvailability?: Prisma.CourtAvailabilityOmit
|
courtAvailability?: Prisma.CourtAvailabilityOmit
|
||||||
courtPriceRule?: Prisma.CourtPriceRuleOmit
|
courtPriceRule?: Prisma.CourtPriceRuleOmit
|
||||||
courtBooking?: Prisma.CourtBookingOmit
|
courtBooking?: Prisma.CourtBookingOmit
|
||||||
onboardingRequest?: Prisma.OnboardingRequestOmit
|
courtBookingLog?: Prisma.CourtBookingLogOmit
|
||||||
|
passwordResetRequest?: Prisma.PasswordResetRequestOmit
|
||||||
plan?: Prisma.PlanOmit
|
plan?: Prisma.PlanOmit
|
||||||
user?: Prisma.UserOmit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Types for Logging */
|
/* Types for Logging */
|
||||||
|
|||||||
@@ -51,16 +51,21 @@ export const AnyNull = runtime.AnyNull
|
|||||||
|
|
||||||
|
|
||||||
export const ModelName = {
|
export const ModelName = {
|
||||||
|
User: 'User',
|
||||||
|
Session: 'Session',
|
||||||
|
Account: 'Account',
|
||||||
|
Verification: 'Verification',
|
||||||
Complex: 'Complex',
|
Complex: 'Complex',
|
||||||
ComplexUser: 'ComplexUser',
|
ComplexUser: 'ComplexUser',
|
||||||
|
ComplexInvitation: 'ComplexInvitation',
|
||||||
Sport: 'Sport',
|
Sport: 'Sport',
|
||||||
Court: 'Court',
|
Court: 'Court',
|
||||||
CourtAvailability: 'CourtAvailability',
|
CourtAvailability: 'CourtAvailability',
|
||||||
CourtPriceRule: 'CourtPriceRule',
|
CourtPriceRule: 'CourtPriceRule',
|
||||||
CourtBooking: 'CourtBooking',
|
CourtBooking: 'CourtBooking',
|
||||||
OnboardingRequest: 'OnboardingRequest',
|
CourtBookingLog: 'CourtBookingLog',
|
||||||
Plan: 'Plan',
|
PasswordResetRequest: 'PasswordResetRequest',
|
||||||
User: 'User'
|
Plan: 'Plan'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
export type ModelName = (typeof ModelName)[keyof typeof ModelName]
|
||||||
@@ -79,10 +84,73 @@ export const TransactionIsolationLevel = runtime.makeStrictEnum({
|
|||||||
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
export type TransactionIsolationLevel = (typeof TransactionIsolationLevel)[keyof typeof TransactionIsolationLevel]
|
||||||
|
|
||||||
|
|
||||||
|
export const UserScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
name: 'name',
|
||||||
|
email: 'email',
|
||||||
|
emailVerified: 'emailVerified',
|
||||||
|
image: 'image',
|
||||||
|
phone: 'phone',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
role: 'role'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const SessionScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
token: 'token',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt',
|
||||||
|
ipAddress: 'ipAddress',
|
||||||
|
userAgent: 'userAgent',
|
||||||
|
userId: 'userId'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type SessionScalarFieldEnum = (typeof SessionScalarFieldEnum)[keyof typeof SessionScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const AccountScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
accountId: 'accountId',
|
||||||
|
providerId: 'providerId',
|
||||||
|
userId: 'userId',
|
||||||
|
accessToken: 'accessToken',
|
||||||
|
refreshToken: 'refreshToken',
|
||||||
|
idToken: 'idToken',
|
||||||
|
accessTokenExpiresAt: 'accessTokenExpiresAt',
|
||||||
|
refreshTokenExpiresAt: 'refreshTokenExpiresAt',
|
||||||
|
scope: 'scope',
|
||||||
|
password: 'password',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type AccountScalarFieldEnum = (typeof AccountScalarFieldEnum)[keyof typeof AccountScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const VerificationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
identifier: 'identifier',
|
||||||
|
value: 'value',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type VerificationScalarFieldEnum = (typeof VerificationScalarFieldEnum)[keyof typeof VerificationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const ComplexScalarFieldEnum = {
|
export const ComplexScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
complexName: 'complexName',
|
complexName: 'complexName',
|
||||||
physicalAddress: 'physicalAddress',
|
physicalAddress: 'physicalAddress',
|
||||||
|
city: 'city',
|
||||||
|
state: 'state',
|
||||||
|
country: 'country',
|
||||||
complexSlug: 'complexSlug',
|
complexSlug: 'complexSlug',
|
||||||
adminEmail: 'adminEmail',
|
adminEmail: 'adminEmail',
|
||||||
planCode: 'planCode',
|
planCode: 'planCode',
|
||||||
@@ -103,6 +171,21 @@ export const ComplexUserScalarFieldEnum = {
|
|||||||
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
export type ComplexUserScalarFieldEnum = (typeof ComplexUserScalarFieldEnum)[keyof typeof ComplexUserScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const ComplexInvitationScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
complexId: 'complexId',
|
||||||
|
email: 'email',
|
||||||
|
tokenHash: 'tokenHash',
|
||||||
|
expiresAt: 'expiresAt',
|
||||||
|
acceptedAt: 'acceptedAt',
|
||||||
|
revokedAt: 'revokedAt',
|
||||||
|
createdAt: 'createdAt',
|
||||||
|
updatedAt: 'updatedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type ComplexInvitationScalarFieldEnum = (typeof ComplexInvitationScalarFieldEnum)[keyof typeof ComplexInvitationScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const SportScalarFieldEnum = {
|
export const SportScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
name: 'name',
|
name: 'name',
|
||||||
@@ -165,6 +248,7 @@ export const CourtBookingScalarFieldEnum = {
|
|||||||
endTime: 'endTime',
|
endTime: 'endTime',
|
||||||
customerName: 'customerName',
|
customerName: 'customerName',
|
||||||
customerPhone: 'customerPhone',
|
customerPhone: 'customerPhone',
|
||||||
|
customerEmail: 'customerEmail',
|
||||||
status: 'status',
|
status: 'status',
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
@@ -173,22 +257,38 @@ export const CourtBookingScalarFieldEnum = {
|
|||||||
export type CourtBookingScalarFieldEnum = (typeof CourtBookingScalarFieldEnum)[keyof typeof CourtBookingScalarFieldEnum]
|
export type CourtBookingScalarFieldEnum = (typeof CourtBookingScalarFieldEnum)[keyof typeof CourtBookingScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const OnboardingRequestScalarFieldEnum = {
|
export const CourtBookingLogScalarFieldEnum = {
|
||||||
|
id: 'id',
|
||||||
|
bookingCode: 'bookingCode',
|
||||||
|
courtId: 'courtId',
|
||||||
|
bookingDate: 'bookingDate',
|
||||||
|
startTime: 'startTime',
|
||||||
|
endTime: 'endTime',
|
||||||
|
previousStatus: 'previousStatus',
|
||||||
|
newStatus: 'newStatus',
|
||||||
|
customerName: 'customerName',
|
||||||
|
customerPhone: 'customerPhone',
|
||||||
|
customerEmail: 'customerEmail',
|
||||||
|
changedAt: 'changedAt'
|
||||||
|
} as const
|
||||||
|
|
||||||
|
export type CourtBookingLogScalarFieldEnum = (typeof CourtBookingLogScalarFieldEnum)[keyof typeof CourtBookingLogScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
|
export const PasswordResetRequestScalarFieldEnum = {
|
||||||
id: 'id',
|
id: 'id',
|
||||||
fullName: 'fullName',
|
|
||||||
email: 'email',
|
email: 'email',
|
||||||
otpHash: 'otpHash',
|
otpHash: 'otpHash',
|
||||||
otpExpiresAt: 'otpExpiresAt',
|
otpExpiresAt: 'otpExpiresAt',
|
||||||
otpAttempts: 'otpAttempts',
|
otpAttempts: 'otpAttempts',
|
||||||
otpLastSentAt: 'otpLastSentAt',
|
otpLastSentAt: 'otpLastSentAt',
|
||||||
otpResendCount: 'otpResendCount',
|
otpResendCount: 'otpResendCount',
|
||||||
emailVerifiedAt: 'emailVerifiedAt',
|
usedAt: 'usedAt',
|
||||||
completedAt: 'completedAt',
|
|
||||||
createdAt: 'createdAt',
|
createdAt: 'createdAt',
|
||||||
updatedAt: 'updatedAt'
|
updatedAt: 'updatedAt'
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export type OnboardingRequestScalarFieldEnum = (typeof OnboardingRequestScalarFieldEnum)[keyof typeof OnboardingRequestScalarFieldEnum]
|
export type PasswordResetRequestScalarFieldEnum = (typeof PasswordResetRequestScalarFieldEnum)[keyof typeof PasswordResetRequestScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const PlanScalarFieldEnum = {
|
export const PlanScalarFieldEnum = {
|
||||||
@@ -202,18 +302,6 @@ export const PlanScalarFieldEnum = {
|
|||||||
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
export type PlanScalarFieldEnum = (typeof PlanScalarFieldEnum)[keyof typeof PlanScalarFieldEnum]
|
||||||
|
|
||||||
|
|
||||||
export const UserScalarFieldEnum = {
|
|
||||||
id: 'id',
|
|
||||||
supabaseUserId: 'supabaseUserId',
|
|
||||||
email: 'email',
|
|
||||||
fullName: 'fullName',
|
|
||||||
createdAt: 'createdAt',
|
|
||||||
updatedAt: 'updatedAt'
|
|
||||||
} as const
|
|
||||||
|
|
||||||
export type UserScalarFieldEnum = (typeof UserScalarFieldEnum)[keyof typeof UserScalarFieldEnum]
|
|
||||||
|
|
||||||
|
|
||||||
export const SortOrder = {
|
export const SortOrder = {
|
||||||
asc: 'asc',
|
asc: 'asc',
|
||||||
desc: 'desc'
|
desc: 'desc'
|
||||||
|
|||||||
@@ -8,14 +8,19 @@
|
|||||||
*
|
*
|
||||||
* 🟢 You can import this file directly.
|
* 🟢 You can import this file directly.
|
||||||
*/
|
*/
|
||||||
|
export type * from './models/User'
|
||||||
|
export type * from './models/Session'
|
||||||
|
export type * from './models/Account'
|
||||||
|
export type * from './models/Verification'
|
||||||
export type * from './models/Complex'
|
export type * from './models/Complex'
|
||||||
export type * from './models/ComplexUser'
|
export type * from './models/ComplexUser'
|
||||||
|
export type * from './models/ComplexInvitation'
|
||||||
export type * from './models/Sport'
|
export type * from './models/Sport'
|
||||||
export type * from './models/Court'
|
export type * from './models/Court'
|
||||||
export type * from './models/CourtAvailability'
|
export type * from './models/CourtAvailability'
|
||||||
export type * from './models/CourtPriceRule'
|
export type * from './models/CourtPriceRule'
|
||||||
export type * from './models/CourtBooking'
|
export type * from './models/CourtBooking'
|
||||||
export type * from './models/OnboardingRequest'
|
export type * from './models/CourtBookingLog'
|
||||||
|
export type * from './models/PasswordResetRequest'
|
||||||
export type * from './models/Plan'
|
export type * from './models/Plan'
|
||||||
export type * from './models/User'
|
|
||||||
export type * from './commonInputTypes'
|
export type * from './commonInputTypes'
|
||||||
@@ -28,6 +28,9 @@ export type ComplexMinAggregateOutputType = {
|
|||||||
id: string | null
|
id: string | null
|
||||||
complexName: string | null
|
complexName: string | null
|
||||||
physicalAddress: string | null
|
physicalAddress: string | null
|
||||||
|
city: string | null
|
||||||
|
state: string | null
|
||||||
|
country: string | null
|
||||||
complexSlug: string | null
|
complexSlug: string | null
|
||||||
adminEmail: string | null
|
adminEmail: string | null
|
||||||
planCode: string | null
|
planCode: string | null
|
||||||
@@ -39,6 +42,9 @@ export type ComplexMaxAggregateOutputType = {
|
|||||||
id: string | null
|
id: string | null
|
||||||
complexName: string | null
|
complexName: string | null
|
||||||
physicalAddress: string | null
|
physicalAddress: string | null
|
||||||
|
city: string | null
|
||||||
|
state: string | null
|
||||||
|
country: string | null
|
||||||
complexSlug: string | null
|
complexSlug: string | null
|
||||||
adminEmail: string | null
|
adminEmail: string | null
|
||||||
planCode: string | null
|
planCode: string | null
|
||||||
@@ -50,6 +56,9 @@ export type ComplexCountAggregateOutputType = {
|
|||||||
id: number
|
id: number
|
||||||
complexName: number
|
complexName: number
|
||||||
physicalAddress: number
|
physicalAddress: number
|
||||||
|
city: number
|
||||||
|
state: number
|
||||||
|
country: number
|
||||||
complexSlug: number
|
complexSlug: number
|
||||||
adminEmail: number
|
adminEmail: number
|
||||||
planCode: number
|
planCode: number
|
||||||
@@ -63,6 +72,9 @@ export type ComplexMinAggregateInputType = {
|
|||||||
id?: true
|
id?: true
|
||||||
complexName?: true
|
complexName?: true
|
||||||
physicalAddress?: true
|
physicalAddress?: true
|
||||||
|
city?: true
|
||||||
|
state?: true
|
||||||
|
country?: true
|
||||||
complexSlug?: true
|
complexSlug?: true
|
||||||
adminEmail?: true
|
adminEmail?: true
|
||||||
planCode?: true
|
planCode?: true
|
||||||
@@ -74,6 +86,9 @@ export type ComplexMaxAggregateInputType = {
|
|||||||
id?: true
|
id?: true
|
||||||
complexName?: true
|
complexName?: true
|
||||||
physicalAddress?: true
|
physicalAddress?: true
|
||||||
|
city?: true
|
||||||
|
state?: true
|
||||||
|
country?: true
|
||||||
complexSlug?: true
|
complexSlug?: true
|
||||||
adminEmail?: true
|
adminEmail?: true
|
||||||
planCode?: true
|
planCode?: true
|
||||||
@@ -85,6 +100,9 @@ export type ComplexCountAggregateInputType = {
|
|||||||
id?: true
|
id?: true
|
||||||
complexName?: true
|
complexName?: true
|
||||||
physicalAddress?: true
|
physicalAddress?: true
|
||||||
|
city?: true
|
||||||
|
state?: true
|
||||||
|
country?: true
|
||||||
complexSlug?: true
|
complexSlug?: true
|
||||||
adminEmail?: true
|
adminEmail?: true
|
||||||
planCode?: true
|
planCode?: true
|
||||||
@@ -169,6 +187,9 @@ export type ComplexGroupByOutputType = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress: string | null
|
physicalAddress: string | null
|
||||||
|
city: string | null
|
||||||
|
state: string | null
|
||||||
|
country: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
planCode: string | null
|
planCode: string | null
|
||||||
@@ -201,6 +222,9 @@ export type ComplexWhereInput = {
|
|||||||
id?: Prisma.UuidFilter<"Complex"> | string
|
id?: Prisma.UuidFilter<"Complex"> | string
|
||||||
complexName?: Prisma.StringFilter<"Complex"> | string
|
complexName?: Prisma.StringFilter<"Complex"> | string
|
||||||
physicalAddress?: Prisma.StringNullableFilter<"Complex"> | string | null
|
physicalAddress?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
city?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
state?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
country?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
complexSlug?: Prisma.StringFilter<"Complex"> | string
|
complexSlug?: Prisma.StringFilter<"Complex"> | string
|
||||||
adminEmail?: Prisma.StringFilter<"Complex"> | string
|
adminEmail?: Prisma.StringFilter<"Complex"> | string
|
||||||
planCode?: Prisma.StringNullableFilter<"Complex"> | string | null
|
planCode?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
@@ -208,6 +232,7 @@ export type ComplexWhereInput = {
|
|||||||
updatedAt?: Prisma.DateTimeFilter<"Complex"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"Complex"> | Date | string
|
||||||
plan?: Prisma.XOR<Prisma.PlanNullableScalarRelationFilter, Prisma.PlanWhereInput> | null
|
plan?: Prisma.XOR<Prisma.PlanNullableScalarRelationFilter, Prisma.PlanWhereInput> | null
|
||||||
users?: Prisma.ComplexUserListRelationFilter
|
users?: Prisma.ComplexUserListRelationFilter
|
||||||
|
invitations?: Prisma.ComplexInvitationListRelationFilter
|
||||||
courts?: Prisma.CourtListRelationFilter
|
courts?: Prisma.CourtListRelationFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,6 +240,9 @@ export type ComplexOrderByWithRelationInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
complexName?: Prisma.SortOrder
|
complexName?: Prisma.SortOrder
|
||||||
physicalAddress?: Prisma.SortOrderInput | Prisma.SortOrder
|
physicalAddress?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
city?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
state?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
country?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
complexSlug?: Prisma.SortOrder
|
complexSlug?: Prisma.SortOrder
|
||||||
adminEmail?: Prisma.SortOrder
|
adminEmail?: Prisma.SortOrder
|
||||||
planCode?: Prisma.SortOrderInput | Prisma.SortOrder
|
planCode?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
@@ -222,6 +250,7 @@ export type ComplexOrderByWithRelationInput = {
|
|||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
plan?: Prisma.PlanOrderByWithRelationInput
|
plan?: Prisma.PlanOrderByWithRelationInput
|
||||||
users?: Prisma.ComplexUserOrderByRelationAggregateInput
|
users?: Prisma.ComplexUserOrderByRelationAggregateInput
|
||||||
|
invitations?: Prisma.ComplexInvitationOrderByRelationAggregateInput
|
||||||
courts?: Prisma.CourtOrderByRelationAggregateInput
|
courts?: Prisma.CourtOrderByRelationAggregateInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,12 +262,16 @@ export type ComplexWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
NOT?: Prisma.ComplexWhereInput | Prisma.ComplexWhereInput[]
|
NOT?: Prisma.ComplexWhereInput | Prisma.ComplexWhereInput[]
|
||||||
complexName?: Prisma.StringFilter<"Complex"> | string
|
complexName?: Prisma.StringFilter<"Complex"> | string
|
||||||
physicalAddress?: Prisma.StringNullableFilter<"Complex"> | string | null
|
physicalAddress?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
city?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
state?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
country?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
adminEmail?: Prisma.StringFilter<"Complex"> | string
|
adminEmail?: Prisma.StringFilter<"Complex"> | string
|
||||||
planCode?: Prisma.StringNullableFilter<"Complex"> | string | null
|
planCode?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
createdAt?: Prisma.DateTimeFilter<"Complex"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"Complex"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"Complex"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"Complex"> | Date | string
|
||||||
plan?: Prisma.XOR<Prisma.PlanNullableScalarRelationFilter, Prisma.PlanWhereInput> | null
|
plan?: Prisma.XOR<Prisma.PlanNullableScalarRelationFilter, Prisma.PlanWhereInput> | null
|
||||||
users?: Prisma.ComplexUserListRelationFilter
|
users?: Prisma.ComplexUserListRelationFilter
|
||||||
|
invitations?: Prisma.ComplexInvitationListRelationFilter
|
||||||
courts?: Prisma.CourtListRelationFilter
|
courts?: Prisma.CourtListRelationFilter
|
||||||
}, "id" | "complexSlug">
|
}, "id" | "complexSlug">
|
||||||
|
|
||||||
@@ -246,6 +279,9 @@ export type ComplexOrderByWithAggregationInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
complexName?: Prisma.SortOrder
|
complexName?: Prisma.SortOrder
|
||||||
physicalAddress?: Prisma.SortOrderInput | Prisma.SortOrder
|
physicalAddress?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
city?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
state?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
country?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
complexSlug?: Prisma.SortOrder
|
complexSlug?: Prisma.SortOrder
|
||||||
adminEmail?: Prisma.SortOrder
|
adminEmail?: Prisma.SortOrder
|
||||||
planCode?: Prisma.SortOrderInput | Prisma.SortOrder
|
planCode?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
@@ -263,6 +299,9 @@ export type ComplexScalarWhereWithAggregatesInput = {
|
|||||||
id?: Prisma.UuidWithAggregatesFilter<"Complex"> | string
|
id?: Prisma.UuidWithAggregatesFilter<"Complex"> | string
|
||||||
complexName?: Prisma.StringWithAggregatesFilter<"Complex"> | string
|
complexName?: Prisma.StringWithAggregatesFilter<"Complex"> | string
|
||||||
physicalAddress?: Prisma.StringNullableWithAggregatesFilter<"Complex"> | string | null
|
physicalAddress?: Prisma.StringNullableWithAggregatesFilter<"Complex"> | string | null
|
||||||
|
city?: Prisma.StringNullableWithAggregatesFilter<"Complex"> | string | null
|
||||||
|
state?: Prisma.StringNullableWithAggregatesFilter<"Complex"> | string | null
|
||||||
|
country?: Prisma.StringNullableWithAggregatesFilter<"Complex"> | string | null
|
||||||
complexSlug?: Prisma.StringWithAggregatesFilter<"Complex"> | string
|
complexSlug?: Prisma.StringWithAggregatesFilter<"Complex"> | string
|
||||||
adminEmail?: Prisma.StringWithAggregatesFilter<"Complex"> | string
|
adminEmail?: Prisma.StringWithAggregatesFilter<"Complex"> | string
|
||||||
planCode?: Prisma.StringNullableWithAggregatesFilter<"Complex"> | string | null
|
planCode?: Prisma.StringNullableWithAggregatesFilter<"Complex"> | string | null
|
||||||
@@ -274,12 +313,16 @@ export type ComplexCreateInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
|
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
|
||||||
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
|
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
|
||||||
|
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
|
||||||
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
|
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,12 +330,16 @@ export type ComplexUncheckedCreateInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
planCode?: string | null
|
planCode?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
|
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
|
||||||
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
|
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,12 +347,16 @@ export type ComplexUpdateInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
|
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
|
||||||
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
|
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
|
||||||
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
|
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,12 +364,16 @@ export type ComplexUncheckedUpdateInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
|
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
|
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +381,9 @@ export type ComplexCreateManyInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
planCode?: string | null
|
planCode?: string | null
|
||||||
@@ -337,6 +395,9 @@ export type ComplexUpdateManyMutationInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -347,6 +408,9 @@ export type ComplexUncheckedUpdateManyInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
@@ -358,6 +422,9 @@ export type ComplexCountOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
complexName?: Prisma.SortOrder
|
complexName?: Prisma.SortOrder
|
||||||
physicalAddress?: Prisma.SortOrder
|
physicalAddress?: Prisma.SortOrder
|
||||||
|
city?: Prisma.SortOrder
|
||||||
|
state?: Prisma.SortOrder
|
||||||
|
country?: Prisma.SortOrder
|
||||||
complexSlug?: Prisma.SortOrder
|
complexSlug?: Prisma.SortOrder
|
||||||
adminEmail?: Prisma.SortOrder
|
adminEmail?: Prisma.SortOrder
|
||||||
planCode?: Prisma.SortOrder
|
planCode?: Prisma.SortOrder
|
||||||
@@ -369,6 +436,9 @@ export type ComplexMaxOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
complexName?: Prisma.SortOrder
|
complexName?: Prisma.SortOrder
|
||||||
physicalAddress?: Prisma.SortOrder
|
physicalAddress?: Prisma.SortOrder
|
||||||
|
city?: Prisma.SortOrder
|
||||||
|
state?: Prisma.SortOrder
|
||||||
|
country?: Prisma.SortOrder
|
||||||
complexSlug?: Prisma.SortOrder
|
complexSlug?: Prisma.SortOrder
|
||||||
adminEmail?: Prisma.SortOrder
|
adminEmail?: Prisma.SortOrder
|
||||||
planCode?: Prisma.SortOrder
|
planCode?: Prisma.SortOrder
|
||||||
@@ -380,6 +450,9 @@ export type ComplexMinOrderByAggregateInput = {
|
|||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
complexName?: Prisma.SortOrder
|
complexName?: Prisma.SortOrder
|
||||||
physicalAddress?: Prisma.SortOrder
|
physicalAddress?: Prisma.SortOrder
|
||||||
|
city?: Prisma.SortOrder
|
||||||
|
state?: Prisma.SortOrder
|
||||||
|
country?: Prisma.SortOrder
|
||||||
complexSlug?: Prisma.SortOrder
|
complexSlug?: Prisma.SortOrder
|
||||||
adminEmail?: Prisma.SortOrder
|
adminEmail?: Prisma.SortOrder
|
||||||
planCode?: Prisma.SortOrder
|
planCode?: Prisma.SortOrder
|
||||||
@@ -402,18 +475,6 @@ export type ComplexOrderByRelationAggregateInput = {
|
|||||||
_count?: Prisma.SortOrder
|
_count?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type StringFieldUpdateOperationsInput = {
|
|
||||||
set?: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type NullableStringFieldUpdateOperationsInput = {
|
|
||||||
set?: string | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export type DateTimeFieldUpdateOperationsInput = {
|
|
||||||
set?: Date | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexCreateNestedOneWithoutUsersInput = {
|
export type ComplexCreateNestedOneWithoutUsersInput = {
|
||||||
create?: Prisma.XOR<Prisma.ComplexCreateWithoutUsersInput, Prisma.ComplexUncheckedCreateWithoutUsersInput>
|
create?: Prisma.XOR<Prisma.ComplexCreateWithoutUsersInput, Prisma.ComplexUncheckedCreateWithoutUsersInput>
|
||||||
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutUsersInput
|
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutUsersInput
|
||||||
@@ -428,6 +489,20 @@ export type ComplexUpdateOneRequiredWithoutUsersNestedInput = {
|
|||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.ComplexUpdateToOneWithWhereWithoutUsersInput, Prisma.ComplexUpdateWithoutUsersInput>, Prisma.ComplexUncheckedUpdateWithoutUsersInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.ComplexUpdateToOneWithWhereWithoutUsersInput, Prisma.ComplexUpdateWithoutUsersInput>, Prisma.ComplexUncheckedUpdateWithoutUsersInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ComplexCreateNestedOneWithoutInvitationsInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ComplexCreateWithoutInvitationsInput, Prisma.ComplexUncheckedCreateWithoutInvitationsInput>
|
||||||
|
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutInvitationsInput
|
||||||
|
connect?: Prisma.ComplexWhereUniqueInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUpdateOneRequiredWithoutInvitationsNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ComplexCreateWithoutInvitationsInput, Prisma.ComplexUncheckedCreateWithoutInvitationsInput>
|
||||||
|
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutInvitationsInput
|
||||||
|
upsert?: Prisma.ComplexUpsertWithoutInvitationsInput
|
||||||
|
connect?: Prisma.ComplexWhereUniqueInput
|
||||||
|
update?: Prisma.XOR<Prisma.XOR<Prisma.ComplexUpdateToOneWithWhereWithoutInvitationsInput, Prisma.ComplexUpdateWithoutInvitationsInput>, Prisma.ComplexUncheckedUpdateWithoutInvitationsInput>
|
||||||
|
}
|
||||||
|
|
||||||
export type ComplexCreateNestedOneWithoutCourtsInput = {
|
export type ComplexCreateNestedOneWithoutCourtsInput = {
|
||||||
create?: Prisma.XOR<Prisma.ComplexCreateWithoutCourtsInput, Prisma.ComplexUncheckedCreateWithoutCourtsInput>
|
create?: Prisma.XOR<Prisma.ComplexCreateWithoutCourtsInput, Prisma.ComplexUncheckedCreateWithoutCourtsInput>
|
||||||
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutCourtsInput
|
connectOrCreate?: Prisma.ComplexCreateOrConnectWithoutCourtsInput
|
||||||
@@ -488,11 +563,15 @@ export type ComplexCreateWithoutUsersInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
|
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
|
||||||
|
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
|
||||||
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
|
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,11 +579,15 @@ export type ComplexUncheckedCreateWithoutUsersInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
planCode?: string | null
|
planCode?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
|
||||||
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
|
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,11 +611,15 @@ export type ComplexUpdateWithoutUsersInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
|
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
|
||||||
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
|
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,11 +627,95 @@ export type ComplexUncheckedUpdateWithoutUsersInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
|
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexCreateWithoutInvitationsInput = {
|
||||||
|
id: string
|
||||||
|
complexName: string
|
||||||
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
|
complexSlug: string
|
||||||
|
adminEmail: string
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
|
||||||
|
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
|
||||||
|
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUncheckedCreateWithoutInvitationsInput = {
|
||||||
|
id: string
|
||||||
|
complexName: string
|
||||||
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
|
complexSlug: string
|
||||||
|
adminEmail: string
|
||||||
|
planCode?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
|
||||||
|
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexCreateOrConnectWithoutInvitationsInput = {
|
||||||
|
where: Prisma.ComplexWhereUniqueInput
|
||||||
|
create: Prisma.XOR<Prisma.ComplexCreateWithoutInvitationsInput, Prisma.ComplexUncheckedCreateWithoutInvitationsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUpsertWithoutInvitationsInput = {
|
||||||
|
update: Prisma.XOR<Prisma.ComplexUpdateWithoutInvitationsInput, Prisma.ComplexUncheckedUpdateWithoutInvitationsInput>
|
||||||
|
create: Prisma.XOR<Prisma.ComplexCreateWithoutInvitationsInput, Prisma.ComplexUncheckedCreateWithoutInvitationsInput>
|
||||||
|
where?: Prisma.ComplexWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUpdateToOneWithWhereWithoutInvitationsInput = {
|
||||||
|
where?: Prisma.ComplexWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.ComplexUpdateWithoutInvitationsInput, Prisma.ComplexUncheckedUpdateWithoutInvitationsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUpdateWithoutInvitationsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
|
||||||
|
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
|
||||||
|
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUncheckedUpdateWithoutInvitationsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
|
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,24 +723,32 @@ export type ComplexCreateWithoutCourtsInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
|
plan?: Prisma.PlanCreateNestedOneWithoutComplexesInput
|
||||||
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
|
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
|
||||||
|
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUncheckedCreateWithoutCourtsInput = {
|
export type ComplexUncheckedCreateWithoutCourtsInput = {
|
||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
planCode?: string | null
|
planCode?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
|
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexCreateOrConnectWithoutCourtsInput = {
|
export type ComplexCreateOrConnectWithoutCourtsInput = {
|
||||||
@@ -592,35 +771,47 @@ export type ComplexUpdateWithoutCourtsInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
|
plan?: Prisma.PlanUpdateOneWithoutComplexesNestedInput
|
||||||
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
|
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUncheckedUpdateWithoutCourtsInput = {
|
export type ComplexUncheckedUpdateWithoutCourtsInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
planCode?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
|
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexCreateWithoutPlanInput = {
|
export type ComplexCreateWithoutPlanInput = {
|
||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
|
users?: Prisma.ComplexUserCreateNestedManyWithoutComplexInput
|
||||||
|
invitations?: Prisma.ComplexInvitationCreateNestedManyWithoutComplexInput
|
||||||
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
|
courts?: Prisma.CourtCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,11 +819,15 @@ export type ComplexUncheckedCreateWithoutPlanInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
|
users?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutComplexInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedCreateNestedManyWithoutComplexInput
|
||||||
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
|
courts?: Prisma.CourtUncheckedCreateNestedManyWithoutComplexInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -669,6 +864,9 @@ export type ComplexScalarWhereInput = {
|
|||||||
id?: Prisma.UuidFilter<"Complex"> | string
|
id?: Prisma.UuidFilter<"Complex"> | string
|
||||||
complexName?: Prisma.StringFilter<"Complex"> | string
|
complexName?: Prisma.StringFilter<"Complex"> | string
|
||||||
physicalAddress?: Prisma.StringNullableFilter<"Complex"> | string | null
|
physicalAddress?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
city?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
state?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
|
country?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
complexSlug?: Prisma.StringFilter<"Complex"> | string
|
complexSlug?: Prisma.StringFilter<"Complex"> | string
|
||||||
adminEmail?: Prisma.StringFilter<"Complex"> | string
|
adminEmail?: Prisma.StringFilter<"Complex"> | string
|
||||||
planCode?: Prisma.StringNullableFilter<"Complex"> | string | null
|
planCode?: Prisma.StringNullableFilter<"Complex"> | string | null
|
||||||
@@ -680,6 +878,9 @@ export type ComplexCreateManyPlanInput = {
|
|||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null
|
||||||
|
city?: string | null
|
||||||
|
state?: string | null
|
||||||
|
country?: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
@@ -690,11 +891,15 @@ export type ComplexUpdateWithoutPlanInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
|
users?: Prisma.ComplexUserUpdateManyWithoutComplexNestedInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUpdateManyWithoutComplexNestedInput
|
||||||
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
|
courts?: Prisma.CourtUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -702,11 +907,15 @@ export type ComplexUncheckedUpdateWithoutPlanInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
|
users?: Prisma.ComplexUserUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
|
invitations?: Prisma.ComplexInvitationUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
|
courts?: Prisma.CourtUncheckedUpdateManyWithoutComplexNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,6 +923,9 @@ export type ComplexUncheckedUpdateManyWithoutPlanInput = {
|
|||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
complexName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
physicalAddress?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
city?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
state?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
country?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
complexSlug?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
adminEmail?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -727,11 +939,13 @@ export type ComplexUncheckedUpdateManyWithoutPlanInput = {
|
|||||||
|
|
||||||
export type ComplexCountOutputType = {
|
export type ComplexCountOutputType = {
|
||||||
users: number
|
users: number
|
||||||
|
invitations: number
|
||||||
courts: number
|
courts: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ComplexCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
users?: boolean | ComplexCountOutputTypeCountUsersArgs
|
users?: boolean | ComplexCountOutputTypeCountUsersArgs
|
||||||
|
invitations?: boolean | ComplexCountOutputTypeCountInvitationsArgs
|
||||||
courts?: boolean | ComplexCountOutputTypeCountCourtsArgs
|
courts?: boolean | ComplexCountOutputTypeCountCourtsArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,6 +966,13 @@ export type ComplexCountOutputTypeCountUsersArgs<ExtArgs extends runtime.Types.E
|
|||||||
where?: Prisma.ComplexUserWhereInput
|
where?: Prisma.ComplexUserWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ComplexCountOutputType without action
|
||||||
|
*/
|
||||||
|
export type ComplexCountOutputTypeCountInvitationsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
where?: Prisma.ComplexInvitationWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ComplexCountOutputType without action
|
* ComplexCountOutputType without action
|
||||||
*/
|
*/
|
||||||
@@ -764,6 +985,9 @@ export type ComplexSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs
|
|||||||
id?: boolean
|
id?: boolean
|
||||||
complexName?: boolean
|
complexName?: boolean
|
||||||
physicalAddress?: boolean
|
physicalAddress?: boolean
|
||||||
|
city?: boolean
|
||||||
|
state?: boolean
|
||||||
|
country?: boolean
|
||||||
complexSlug?: boolean
|
complexSlug?: boolean
|
||||||
adminEmail?: boolean
|
adminEmail?: boolean
|
||||||
planCode?: boolean
|
planCode?: boolean
|
||||||
@@ -771,6 +995,7 @@ export type ComplexSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs
|
|||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
plan?: boolean | Prisma.Complex$planArgs<ExtArgs>
|
plan?: boolean | Prisma.Complex$planArgs<ExtArgs>
|
||||||
users?: boolean | Prisma.Complex$usersArgs<ExtArgs>
|
users?: boolean | Prisma.Complex$usersArgs<ExtArgs>
|
||||||
|
invitations?: boolean | Prisma.Complex$invitationsArgs<ExtArgs>
|
||||||
courts?: boolean | Prisma.Complex$courtsArgs<ExtArgs>
|
courts?: boolean | Prisma.Complex$courtsArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.ComplexCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ComplexCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["complex"]>
|
}, ExtArgs["result"]["complex"]>
|
||||||
@@ -779,6 +1004,9 @@ export type ComplexSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Exten
|
|||||||
id?: boolean
|
id?: boolean
|
||||||
complexName?: boolean
|
complexName?: boolean
|
||||||
physicalAddress?: boolean
|
physicalAddress?: boolean
|
||||||
|
city?: boolean
|
||||||
|
state?: boolean
|
||||||
|
country?: boolean
|
||||||
complexSlug?: boolean
|
complexSlug?: boolean
|
||||||
adminEmail?: boolean
|
adminEmail?: boolean
|
||||||
planCode?: boolean
|
planCode?: boolean
|
||||||
@@ -791,6 +1019,9 @@ export type ComplexSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Exten
|
|||||||
id?: boolean
|
id?: boolean
|
||||||
complexName?: boolean
|
complexName?: boolean
|
||||||
physicalAddress?: boolean
|
physicalAddress?: boolean
|
||||||
|
city?: boolean
|
||||||
|
state?: boolean
|
||||||
|
country?: boolean
|
||||||
complexSlug?: boolean
|
complexSlug?: boolean
|
||||||
adminEmail?: boolean
|
adminEmail?: boolean
|
||||||
planCode?: boolean
|
planCode?: boolean
|
||||||
@@ -803,6 +1034,9 @@ export type ComplexSelectScalar = {
|
|||||||
id?: boolean
|
id?: boolean
|
||||||
complexName?: boolean
|
complexName?: boolean
|
||||||
physicalAddress?: boolean
|
physicalAddress?: boolean
|
||||||
|
city?: boolean
|
||||||
|
state?: boolean
|
||||||
|
country?: boolean
|
||||||
complexSlug?: boolean
|
complexSlug?: boolean
|
||||||
adminEmail?: boolean
|
adminEmail?: boolean
|
||||||
planCode?: boolean
|
planCode?: boolean
|
||||||
@@ -810,10 +1044,11 @@ export type ComplexSelectScalar = {
|
|||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "complexName" | "physicalAddress" | "complexSlug" | "adminEmail" | "planCode" | "createdAt" | "updatedAt", ExtArgs["result"]["complex"]>
|
export type ComplexOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "complexName" | "physicalAddress" | "city" | "state" | "country" | "complexSlug" | "adminEmail" | "planCode" | "createdAt" | "updatedAt", ExtArgs["result"]["complex"]>
|
||||||
export type ComplexInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type ComplexInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
plan?: boolean | Prisma.Complex$planArgs<ExtArgs>
|
plan?: boolean | Prisma.Complex$planArgs<ExtArgs>
|
||||||
users?: boolean | Prisma.Complex$usersArgs<ExtArgs>
|
users?: boolean | Prisma.Complex$usersArgs<ExtArgs>
|
||||||
|
invitations?: boolean | Prisma.Complex$invitationsArgs<ExtArgs>
|
||||||
courts?: boolean | Prisma.Complex$courtsArgs<ExtArgs>
|
courts?: boolean | Prisma.Complex$courtsArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.ComplexCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.ComplexCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
@@ -829,12 +1064,16 @@ export type $ComplexPayload<ExtArgs extends runtime.Types.Extensions.InternalArg
|
|||||||
objects: {
|
objects: {
|
||||||
plan: Prisma.$PlanPayload<ExtArgs> | null
|
plan: Prisma.$PlanPayload<ExtArgs> | null
|
||||||
users: Prisma.$ComplexUserPayload<ExtArgs>[]
|
users: Prisma.$ComplexUserPayload<ExtArgs>[]
|
||||||
|
invitations: Prisma.$ComplexInvitationPayload<ExtArgs>[]
|
||||||
courts: Prisma.$CourtPayload<ExtArgs>[]
|
courts: Prisma.$CourtPayload<ExtArgs>[]
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: string
|
id: string
|
||||||
complexName: string
|
complexName: string
|
||||||
physicalAddress: string | null
|
physicalAddress: string | null
|
||||||
|
city: string | null
|
||||||
|
state: string | null
|
||||||
|
country: string | null
|
||||||
complexSlug: string
|
complexSlug: string
|
||||||
adminEmail: string
|
adminEmail: string
|
||||||
planCode: string | null
|
planCode: string | null
|
||||||
@@ -1236,6 +1475,7 @@ export interface Prisma__ComplexClient<T, Null = never, ExtArgs extends runtime.
|
|||||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||||
plan<T extends Prisma.Complex$planArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$planArgs<ExtArgs>>): Prisma.Prisma__PlanClient<runtime.Types.Result.GetResult<Prisma.$PlanPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
plan<T extends Prisma.Complex$planArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$planArgs<ExtArgs>>): Prisma.Prisma__PlanClient<runtime.Types.Result.GetResult<Prisma.$PlanPayload<ExtArgs>, T, "findUniqueOrThrow", GlobalOmitOptions> | null, null, ExtArgs, GlobalOmitOptions>
|
||||||
users<T extends Prisma.Complex$usersArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$usersArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
users<T extends Prisma.Complex$usersArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$usersArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
|
invitations<T extends Prisma.Complex$invitationsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$invitationsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexInvitationPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
courts<T extends Prisma.Complex$courtsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$courtsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CourtPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
courts<T extends Prisma.Complex$courtsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.Complex$courtsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$CourtPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
@@ -1269,6 +1509,9 @@ export interface ComplexFieldRefs {
|
|||||||
readonly id: Prisma.FieldRef<"Complex", 'String'>
|
readonly id: Prisma.FieldRef<"Complex", 'String'>
|
||||||
readonly complexName: Prisma.FieldRef<"Complex", 'String'>
|
readonly complexName: Prisma.FieldRef<"Complex", 'String'>
|
||||||
readonly physicalAddress: Prisma.FieldRef<"Complex", 'String'>
|
readonly physicalAddress: Prisma.FieldRef<"Complex", 'String'>
|
||||||
|
readonly city: Prisma.FieldRef<"Complex", 'String'>
|
||||||
|
readonly state: Prisma.FieldRef<"Complex", 'String'>
|
||||||
|
readonly country: Prisma.FieldRef<"Complex", 'String'>
|
||||||
readonly complexSlug: Prisma.FieldRef<"Complex", 'String'>
|
readonly complexSlug: Prisma.FieldRef<"Complex", 'String'>
|
||||||
readonly adminEmail: Prisma.FieldRef<"Complex", 'String'>
|
readonly adminEmail: Prisma.FieldRef<"Complex", 'String'>
|
||||||
readonly planCode: Prisma.FieldRef<"Complex", 'String'>
|
readonly planCode: Prisma.FieldRef<"Complex", 'String'>
|
||||||
@@ -1717,6 +1960,30 @@ export type Complex$usersArgs<ExtArgs extends runtime.Types.Extensions.InternalA
|
|||||||
distinct?: Prisma.ComplexUserScalarFieldEnum | Prisma.ComplexUserScalarFieldEnum[]
|
distinct?: Prisma.ComplexUserScalarFieldEnum | Prisma.ComplexUserScalarFieldEnum[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Complex.invitations
|
||||||
|
*/
|
||||||
|
export type Complex$invitationsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the ComplexInvitation
|
||||||
|
*/
|
||||||
|
select?: Prisma.ComplexInvitationSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the ComplexInvitation
|
||||||
|
*/
|
||||||
|
omit?: Prisma.ComplexInvitationOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.ComplexInvitationInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.ComplexInvitationWhereInput
|
||||||
|
orderBy?: Prisma.ComplexInvitationOrderByWithRelationInput | Prisma.ComplexInvitationOrderByWithRelationInput[]
|
||||||
|
cursor?: Prisma.ComplexInvitationWhereUniqueInput
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
distinct?: Prisma.ComplexInvitationScalarFieldEnum | Prisma.ComplexInvitationScalarFieldEnum[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complex.courts
|
* Complex.courts
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ export type ComplexUserWhereInput = {
|
|||||||
OR?: Prisma.ComplexUserWhereInput[]
|
OR?: Prisma.ComplexUserWhereInput[]
|
||||||
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
||||||
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
||||||
userId?: Prisma.UuidFilter<"ComplexUser"> | string
|
userId?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
||||||
@@ -193,7 +193,7 @@ export type ComplexUserWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
OR?: Prisma.ComplexUserWhereInput[]
|
OR?: Prisma.ComplexUserWhereInput[]
|
||||||
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
NOT?: Prisma.ComplexUserWhereInput | Prisma.ComplexUserWhereInput[]
|
||||||
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
||||||
userId?: Prisma.UuidFilter<"ComplexUser"> | string
|
userId?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
complex?: Prisma.XOR<Prisma.ComplexScalarRelationFilter, Prisma.ComplexWhereInput>
|
||||||
@@ -215,7 +215,7 @@ export type ComplexUserScalarWhereWithAggregatesInput = {
|
|||||||
OR?: Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
OR?: Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
||||||
NOT?: Prisma.ComplexUserScalarWhereWithAggregatesInput | Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
NOT?: Prisma.ComplexUserScalarWhereWithAggregatesInput | Prisma.ComplexUserScalarWhereWithAggregatesInput[]
|
||||||
complexId?: Prisma.UuidWithAggregatesFilter<"ComplexUser"> | string
|
complexId?: Prisma.UuidWithAggregatesFilter<"ComplexUser"> | string
|
||||||
userId?: Prisma.UuidWithAggregatesFilter<"ComplexUser"> | string
|
userId?: Prisma.StringWithAggregatesFilter<"ComplexUser"> | string
|
||||||
role?: Prisma.EnumComplexUserRoleWithAggregatesFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
role?: Prisma.EnumComplexUserRoleWithAggregatesFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"ComplexUser"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"ComplexUser"> | Date | string
|
||||||
}
|
}
|
||||||
@@ -303,6 +303,48 @@ export type ComplexUserMinOrderByAggregateInput = {
|
|||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ComplexUserCreateNestedManyWithoutUserInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
||||||
|
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
||||||
|
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
||||||
|
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUserUncheckedCreateNestedManyWithoutUserInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
||||||
|
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
||||||
|
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
||||||
|
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUserUpdateManyWithoutUserNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
||||||
|
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
||||||
|
upsert?: Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput[]
|
||||||
|
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
||||||
|
set?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
disconnect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
delete?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
update?: Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput[]
|
||||||
|
updateMany?: Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput | Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput[]
|
||||||
|
deleteMany?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUserUncheckedUpdateManyWithoutUserNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
||||||
|
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
||||||
|
upsert?: Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput[]
|
||||||
|
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
||||||
|
set?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
disconnect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
delete?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
||||||
|
update?: Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput[]
|
||||||
|
updateMany?: Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput | Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput[]
|
||||||
|
deleteMany?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
||||||
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateNestedManyWithoutComplexInput = {
|
export type ComplexUserCreateNestedManyWithoutComplexInput = {
|
||||||
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutComplexInput, Prisma.ComplexUserUncheckedCreateWithoutComplexInput> | Prisma.ComplexUserCreateWithoutComplexInput[] | Prisma.ComplexUserUncheckedCreateWithoutComplexInput[]
|
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutComplexInput, Prisma.ComplexUserUncheckedCreateWithoutComplexInput> | Prisma.ComplexUserCreateWithoutComplexInput[] | Prisma.ComplexUserUncheckedCreateWithoutComplexInput[]
|
||||||
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutComplexInput | Prisma.ComplexUserCreateOrConnectWithoutComplexInput[]
|
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutComplexInput | Prisma.ComplexUserCreateOrConnectWithoutComplexInput[]
|
||||||
@@ -349,46 +391,52 @@ export type EnumComplexUserRoleFieldUpdateOperationsInput = {
|
|||||||
set?: $Enums.ComplexUserRole
|
set?: $Enums.ComplexUserRole
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateNestedManyWithoutUserInput = {
|
export type ComplexUserCreateWithoutUserInput = {
|
||||||
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
role?: $Enums.ComplexUserRole
|
||||||
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
createdAt?: Date | string
|
||||||
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
complex: Prisma.ComplexCreateNestedOneWithoutUsersInput
|
||||||
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedCreateNestedManyWithoutUserInput = {
|
export type ComplexUserUncheckedCreateWithoutUserInput = {
|
||||||
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
complexId: string
|
||||||
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
role?: $Enums.ComplexUserRole
|
||||||
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
createdAt?: Date | string
|
||||||
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUpdateManyWithoutUserNestedInput = {
|
export type ComplexUserCreateOrConnectWithoutUserInput = {
|
||||||
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
where: Prisma.ComplexUserWhereUniqueInput
|
||||||
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
create: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput>
|
||||||
upsert?: Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput[]
|
|
||||||
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
|
||||||
set?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
|
||||||
disconnect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
|
||||||
delete?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
|
||||||
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
|
||||||
update?: Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput[]
|
|
||||||
updateMany?: Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput | Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput[]
|
|
||||||
deleteMany?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateManyWithoutUserNestedInput = {
|
export type ComplexUserCreateManyUserInputEnvelope = {
|
||||||
create?: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput> | Prisma.ComplexUserCreateWithoutUserInput[] | Prisma.ComplexUserUncheckedCreateWithoutUserInput[]
|
data: Prisma.ComplexUserCreateManyUserInput | Prisma.ComplexUserCreateManyUserInput[]
|
||||||
connectOrCreate?: Prisma.ComplexUserCreateOrConnectWithoutUserInput | Prisma.ComplexUserCreateOrConnectWithoutUserInput[]
|
skipDuplicates?: boolean
|
||||||
upsert?: Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpsertWithWhereUniqueWithoutUserInput[]
|
}
|
||||||
createMany?: Prisma.ComplexUserCreateManyUserInputEnvelope
|
|
||||||
set?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
export type ComplexUserUpsertWithWhereUniqueWithoutUserInput = {
|
||||||
disconnect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
where: Prisma.ComplexUserWhereUniqueInput
|
||||||
delete?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
update: Prisma.XOR<Prisma.ComplexUserUpdateWithoutUserInput, Prisma.ComplexUserUncheckedUpdateWithoutUserInput>
|
||||||
connect?: Prisma.ComplexUserWhereUniqueInput | Prisma.ComplexUserWhereUniqueInput[]
|
create: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput>
|
||||||
update?: Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput | Prisma.ComplexUserUpdateWithWhereUniqueWithoutUserInput[]
|
}
|
||||||
updateMany?: Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput | Prisma.ComplexUserUpdateManyWithWhereWithoutUserInput[]
|
|
||||||
deleteMany?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
export type ComplexUserUpdateWithWhereUniqueWithoutUserInput = {
|
||||||
|
where: Prisma.ComplexUserWhereUniqueInput
|
||||||
|
data: Prisma.XOR<Prisma.ComplexUserUpdateWithoutUserInput, Prisma.ComplexUserUncheckedUpdateWithoutUserInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUserUpdateManyWithWhereWithoutUserInput = {
|
||||||
|
where: Prisma.ComplexUserScalarWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.ComplexUserUpdateManyMutationInput, Prisma.ComplexUserUncheckedUpdateManyWithoutUserInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ComplexUserScalarWhereInput = {
|
||||||
|
AND?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
||||||
|
OR?: Prisma.ComplexUserScalarWhereInput[]
|
||||||
|
NOT?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
||||||
|
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
||||||
|
userId?: Prisma.StringFilter<"ComplexUser"> | string
|
||||||
|
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
||||||
|
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateWithoutComplexInput = {
|
export type ComplexUserCreateWithoutComplexInput = {
|
||||||
@@ -429,52 +477,28 @@ export type ComplexUserUpdateManyWithWhereWithoutComplexInput = {
|
|||||||
data: Prisma.XOR<Prisma.ComplexUserUpdateManyMutationInput, Prisma.ComplexUserUncheckedUpdateManyWithoutComplexInput>
|
data: Prisma.XOR<Prisma.ComplexUserUpdateManyMutationInput, Prisma.ComplexUserUncheckedUpdateManyWithoutComplexInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserScalarWhereInput = {
|
export type ComplexUserCreateManyUserInput = {
|
||||||
AND?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
|
||||||
OR?: Prisma.ComplexUserScalarWhereInput[]
|
|
||||||
NOT?: Prisma.ComplexUserScalarWhereInput | Prisma.ComplexUserScalarWhereInput[]
|
|
||||||
complexId?: Prisma.UuidFilter<"ComplexUser"> | string
|
|
||||||
userId?: Prisma.UuidFilter<"ComplexUser"> | string
|
|
||||||
role?: Prisma.EnumComplexUserRoleFilter<"ComplexUser"> | $Enums.ComplexUserRole
|
|
||||||
createdAt?: Prisma.DateTimeFilter<"ComplexUser"> | Date | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexUserCreateWithoutUserInput = {
|
|
||||||
role?: $Enums.ComplexUserRole
|
|
||||||
createdAt?: Date | string
|
|
||||||
complex: Prisma.ComplexCreateNestedOneWithoutUsersInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexUserUncheckedCreateWithoutUserInput = {
|
|
||||||
complexId: string
|
complexId: string
|
||||||
role?: $Enums.ComplexUserRole
|
role?: $Enums.ComplexUserRole
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateOrConnectWithoutUserInput = {
|
export type ComplexUserUpdateWithoutUserInput = {
|
||||||
where: Prisma.ComplexUserWhereUniqueInput
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
create: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput>
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
complex?: Prisma.ComplexUpdateOneRequiredWithoutUsersNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateManyUserInputEnvelope = {
|
export type ComplexUserUncheckedUpdateWithoutUserInput = {
|
||||||
data: Prisma.ComplexUserCreateManyUserInput | Prisma.ComplexUserCreateManyUserInput[]
|
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
skipDuplicates?: boolean
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserUpsertWithWhereUniqueWithoutUserInput = {
|
export type ComplexUserUncheckedUpdateManyWithoutUserInput = {
|
||||||
where: Prisma.ComplexUserWhereUniqueInput
|
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
update: Prisma.XOR<Prisma.ComplexUserUpdateWithoutUserInput, Prisma.ComplexUserUncheckedUpdateWithoutUserInput>
|
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
||||||
create: Prisma.XOR<Prisma.ComplexUserCreateWithoutUserInput, Prisma.ComplexUserUncheckedCreateWithoutUserInput>
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexUserUpdateWithWhereUniqueWithoutUserInput = {
|
|
||||||
where: Prisma.ComplexUserWhereUniqueInput
|
|
||||||
data: Prisma.XOR<Prisma.ComplexUserUpdateWithoutUserInput, Prisma.ComplexUserUncheckedUpdateWithoutUserInput>
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexUserUpdateManyWithWhereWithoutUserInput = {
|
|
||||||
where: Prisma.ComplexUserScalarWhereInput
|
|
||||||
data: Prisma.XOR<Prisma.ComplexUserUpdateManyMutationInput, Prisma.ComplexUserUncheckedUpdateManyWithoutUserInput>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateManyComplexInput = {
|
export type ComplexUserCreateManyComplexInput = {
|
||||||
@@ -501,30 +525,6 @@ export type ComplexUserUncheckedUpdateManyWithoutComplexInput = {
|
|||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ComplexUserCreateManyUserInput = {
|
|
||||||
complexId: string
|
|
||||||
role?: $Enums.ComplexUserRole
|
|
||||||
createdAt?: Date | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexUserUpdateWithoutUserInput = {
|
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
||||||
complex?: Prisma.ComplexUpdateOneRequiredWithoutUsersNestedInput
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateWithoutUserInput = {
|
|
||||||
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComplexUserUncheckedUpdateManyWithoutUserInput = {
|
|
||||||
complexId?: Prisma.StringFieldUpdateOperationsInput | string
|
|
||||||
role?: Prisma.EnumComplexUserRoleFieldUpdateOperationsInput | $Enums.ComplexUserRole
|
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export type ComplexUserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type ComplexUserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export type CourtBookingMinAggregateOutputType = {
|
|||||||
endTime: string | null
|
endTime: string | null
|
||||||
customerName: string | null
|
customerName: string | null
|
||||||
customerPhone: string | null
|
customerPhone: string | null
|
||||||
|
customerEmail: string | null
|
||||||
status: $Enums.CourtBookingStatus | null
|
status: $Enums.CourtBookingStatus | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
@@ -47,6 +48,7 @@ export type CourtBookingMaxAggregateOutputType = {
|
|||||||
endTime: string | null
|
endTime: string | null
|
||||||
customerName: string | null
|
customerName: string | null
|
||||||
customerPhone: string | null
|
customerPhone: string | null
|
||||||
|
customerEmail: string | null
|
||||||
status: $Enums.CourtBookingStatus | null
|
status: $Enums.CourtBookingStatus | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
@@ -61,6 +63,7 @@ export type CourtBookingCountAggregateOutputType = {
|
|||||||
endTime: number
|
endTime: number
|
||||||
customerName: number
|
customerName: number
|
||||||
customerPhone: number
|
customerPhone: number
|
||||||
|
customerEmail: number
|
||||||
status: number
|
status: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
@@ -77,6 +80,7 @@ export type CourtBookingMinAggregateInputType = {
|
|||||||
endTime?: true
|
endTime?: true
|
||||||
customerName?: true
|
customerName?: true
|
||||||
customerPhone?: true
|
customerPhone?: true
|
||||||
|
customerEmail?: true
|
||||||
status?: true
|
status?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
@@ -91,6 +95,7 @@ export type CourtBookingMaxAggregateInputType = {
|
|||||||
endTime?: true
|
endTime?: true
|
||||||
customerName?: true
|
customerName?: true
|
||||||
customerPhone?: true
|
customerPhone?: true
|
||||||
|
customerEmail?: true
|
||||||
status?: true
|
status?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
@@ -105,6 +110,7 @@ export type CourtBookingCountAggregateInputType = {
|
|||||||
endTime?: true
|
endTime?: true
|
||||||
customerName?: true
|
customerName?: true
|
||||||
customerPhone?: true
|
customerPhone?: true
|
||||||
|
customerEmail?: true
|
||||||
status?: true
|
status?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
@@ -192,6 +198,7 @@ export type CourtBookingGroupByOutputType = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail: string | null
|
||||||
status: $Enums.CourtBookingStatus
|
status: $Enums.CourtBookingStatus
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
@@ -227,6 +234,7 @@ export type CourtBookingWhereInput = {
|
|||||||
endTime?: Prisma.StringFilter<"CourtBooking"> | string
|
endTime?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
customerName?: Prisma.StringFilter<"CourtBooking"> | string
|
customerName?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
|
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
|
customerEmail?: Prisma.StringNullableFilter<"CourtBooking"> | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
||||||
@@ -242,6 +250,7 @@ export type CourtBookingOrderByWithRelationInput = {
|
|||||||
endTime?: Prisma.SortOrder
|
endTime?: Prisma.SortOrder
|
||||||
customerName?: Prisma.SortOrder
|
customerName?: Prisma.SortOrder
|
||||||
customerPhone?: Prisma.SortOrder
|
customerPhone?: Prisma.SortOrder
|
||||||
|
customerEmail?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
status?: Prisma.SortOrder
|
status?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -261,6 +270,7 @@ export type CourtBookingWhereUniqueInput = Prisma.AtLeast<{
|
|||||||
endTime?: Prisma.StringFilter<"CourtBooking"> | string
|
endTime?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
customerName?: Prisma.StringFilter<"CourtBooking"> | string
|
customerName?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
|
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
|
customerEmail?: Prisma.StringNullableFilter<"CourtBooking"> | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
||||||
@@ -276,6 +286,7 @@ export type CourtBookingOrderByWithAggregationInput = {
|
|||||||
endTime?: Prisma.SortOrder
|
endTime?: Prisma.SortOrder
|
||||||
customerName?: Prisma.SortOrder
|
customerName?: Prisma.SortOrder
|
||||||
customerPhone?: Prisma.SortOrder
|
customerPhone?: Prisma.SortOrder
|
||||||
|
customerEmail?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
status?: Prisma.SortOrder
|
status?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -296,6 +307,7 @@ export type CourtBookingScalarWhereWithAggregatesInput = {
|
|||||||
endTime?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
|
endTime?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
|
||||||
customerName?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
|
customerName?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
|
||||||
customerPhone?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
|
customerPhone?: Prisma.StringWithAggregatesFilter<"CourtBooking"> | string
|
||||||
|
customerEmail?: Prisma.StringNullableWithAggregatesFilter<"CourtBooking"> | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusWithAggregatesFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusWithAggregatesFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"CourtBooking"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"CourtBooking"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"CourtBooking"> | Date | string
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"CourtBooking"> | Date | string
|
||||||
@@ -309,6 +321,7 @@ export type CourtBookingCreateInput = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail?: string | null
|
||||||
status?: $Enums.CourtBookingStatus
|
status?: $Enums.CourtBookingStatus
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
@@ -324,6 +337,7 @@ export type CourtBookingUncheckedCreateInput = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail?: string | null
|
||||||
status?: $Enums.CourtBookingStatus
|
status?: $Enums.CourtBookingStatus
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
@@ -337,6 +351,7 @@ export type CourtBookingUpdateInput = {
|
|||||||
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
customerEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -352,6 +367,7 @@ export type CourtBookingUncheckedUpdateInput = {
|
|||||||
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
customerEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -366,6 +382,7 @@ export type CourtBookingCreateManyInput = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail?: string | null
|
||||||
status?: $Enums.CourtBookingStatus
|
status?: $Enums.CourtBookingStatus
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
@@ -379,6 +396,7 @@ export type CourtBookingUpdateManyMutationInput = {
|
|||||||
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
customerEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -393,6 +411,7 @@ export type CourtBookingUncheckedUpdateManyInput = {
|
|||||||
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
customerEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -423,6 +442,7 @@ export type CourtBookingCountOrderByAggregateInput = {
|
|||||||
endTime?: Prisma.SortOrder
|
endTime?: Prisma.SortOrder
|
||||||
customerName?: Prisma.SortOrder
|
customerName?: Prisma.SortOrder
|
||||||
customerPhone?: Prisma.SortOrder
|
customerPhone?: Prisma.SortOrder
|
||||||
|
customerEmail?: Prisma.SortOrder
|
||||||
status?: Prisma.SortOrder
|
status?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -437,6 +457,7 @@ export type CourtBookingMaxOrderByAggregateInput = {
|
|||||||
endTime?: Prisma.SortOrder
|
endTime?: Prisma.SortOrder
|
||||||
customerName?: Prisma.SortOrder
|
customerName?: Prisma.SortOrder
|
||||||
customerPhone?: Prisma.SortOrder
|
customerPhone?: Prisma.SortOrder
|
||||||
|
customerEmail?: Prisma.SortOrder
|
||||||
status?: Prisma.SortOrder
|
status?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -451,6 +472,7 @@ export type CourtBookingMinOrderByAggregateInput = {
|
|||||||
endTime?: Prisma.SortOrder
|
endTime?: Prisma.SortOrder
|
||||||
customerName?: Prisma.SortOrder
|
customerName?: Prisma.SortOrder
|
||||||
customerPhone?: Prisma.SortOrder
|
customerPhone?: Prisma.SortOrder
|
||||||
|
customerEmail?: Prisma.SortOrder
|
||||||
status?: Prisma.SortOrder
|
status?: Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
@@ -510,6 +532,7 @@ export type CourtBookingCreateWithoutCourtInput = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail?: string | null
|
||||||
status?: $Enums.CourtBookingStatus
|
status?: $Enums.CourtBookingStatus
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
@@ -523,6 +546,7 @@ export type CourtBookingUncheckedCreateWithoutCourtInput = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail?: string | null
|
||||||
status?: $Enums.CourtBookingStatus
|
status?: $Enums.CourtBookingStatus
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
@@ -566,6 +590,7 @@ export type CourtBookingScalarWhereInput = {
|
|||||||
endTime?: Prisma.StringFilter<"CourtBooking"> | string
|
endTime?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
customerName?: Prisma.StringFilter<"CourtBooking"> | string
|
customerName?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
|
customerPhone?: Prisma.StringFilter<"CourtBooking"> | string
|
||||||
|
customerEmail?: Prisma.StringNullableFilter<"CourtBooking"> | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFilter<"CourtBooking"> | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"CourtBooking"> | Date | string
|
||||||
@@ -579,6 +604,7 @@ export type CourtBookingCreateManyCourtInput = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail?: string | null
|
||||||
status?: $Enums.CourtBookingStatus
|
status?: $Enums.CourtBookingStatus
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
@@ -592,6 +618,7 @@ export type CourtBookingUpdateWithoutCourtInput = {
|
|||||||
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
customerEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -605,6 +632,7 @@ export type CourtBookingUncheckedUpdateWithoutCourtInput = {
|
|||||||
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
customerEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -618,6 +646,7 @@ export type CourtBookingUncheckedUpdateManyWithoutCourtInput = {
|
|||||||
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
endTime?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
customerName?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
customerPhone?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
customerEmail?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
status?: Prisma.EnumCourtBookingStatusFieldUpdateOperationsInput | $Enums.CourtBookingStatus
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
@@ -634,6 +663,7 @@ export type CourtBookingSelect<ExtArgs extends runtime.Types.Extensions.Internal
|
|||||||
endTime?: boolean
|
endTime?: boolean
|
||||||
customerName?: boolean
|
customerName?: boolean
|
||||||
customerPhone?: boolean
|
customerPhone?: boolean
|
||||||
|
customerEmail?: boolean
|
||||||
status?: boolean
|
status?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
@@ -649,6 +679,7 @@ export type CourtBookingSelectCreateManyAndReturn<ExtArgs extends runtime.Types.
|
|||||||
endTime?: boolean
|
endTime?: boolean
|
||||||
customerName?: boolean
|
customerName?: boolean
|
||||||
customerPhone?: boolean
|
customerPhone?: boolean
|
||||||
|
customerEmail?: boolean
|
||||||
status?: boolean
|
status?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
@@ -664,6 +695,7 @@ export type CourtBookingSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.
|
|||||||
endTime?: boolean
|
endTime?: boolean
|
||||||
customerName?: boolean
|
customerName?: boolean
|
||||||
customerPhone?: boolean
|
customerPhone?: boolean
|
||||||
|
customerEmail?: boolean
|
||||||
status?: boolean
|
status?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
@@ -679,12 +711,13 @@ export type CourtBookingSelectScalar = {
|
|||||||
endTime?: boolean
|
endTime?: boolean
|
||||||
customerName?: boolean
|
customerName?: boolean
|
||||||
customerPhone?: boolean
|
customerPhone?: boolean
|
||||||
|
customerEmail?: boolean
|
||||||
status?: boolean
|
status?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CourtBookingOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "bookingCode" | "courtId" | "bookingDate" | "startTime" | "endTime" | "customerName" | "customerPhone" | "status" | "createdAt" | "updatedAt", ExtArgs["result"]["courtBooking"]>
|
export type CourtBookingOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "bookingCode" | "courtId" | "bookingDate" | "startTime" | "endTime" | "customerName" | "customerPhone" | "customerEmail" | "status" | "createdAt" | "updatedAt", ExtArgs["result"]["courtBooking"]>
|
||||||
export type CourtBookingInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type CourtBookingInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
|
court?: boolean | Prisma.CourtDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
@@ -709,6 +742,7 @@ export type $CourtBookingPayload<ExtArgs extends runtime.Types.Extensions.Intern
|
|||||||
endTime: string
|
endTime: string
|
||||||
customerName: string
|
customerName: string
|
||||||
customerPhone: string
|
customerPhone: string
|
||||||
|
customerEmail: string | null
|
||||||
status: $Enums.CourtBookingStatus
|
status: $Enums.CourtBookingStatus
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
@@ -1144,6 +1178,7 @@ export interface CourtBookingFieldRefs {
|
|||||||
readonly endTime: Prisma.FieldRef<"CourtBooking", 'String'>
|
readonly endTime: Prisma.FieldRef<"CourtBooking", 'String'>
|
||||||
readonly customerName: Prisma.FieldRef<"CourtBooking", 'String'>
|
readonly customerName: Prisma.FieldRef<"CourtBooking", 'String'>
|
||||||
readonly customerPhone: Prisma.FieldRef<"CourtBooking", 'String'>
|
readonly customerPhone: Prisma.FieldRef<"CourtBooking", 'String'>
|
||||||
|
readonly customerEmail: Prisma.FieldRef<"CourtBooking", 'String'>
|
||||||
readonly status: Prisma.FieldRef<"CourtBooking", 'CourtBookingStatus'>
|
readonly status: Prisma.FieldRef<"CourtBooking", 'CourtBookingStatus'>
|
||||||
readonly createdAt: Prisma.FieldRef<"CourtBooking", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"CourtBooking", 'DateTime'>
|
||||||
readonly updatedAt: Prisma.FieldRef<"CourtBooking", 'DateTime'>
|
readonly updatedAt: Prisma.FieldRef<"CourtBooking", 'DateTime'>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -339,10 +339,6 @@ export type SportScalarRelationFilter = {
|
|||||||
isNot?: Prisma.SportWhereInput
|
isNot?: Prisma.SportWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BoolFieldUpdateOperationsInput = {
|
|
||||||
set?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SportCreateNestedOneWithoutCourtsInput = {
|
export type SportCreateNestedOneWithoutCourtsInput = {
|
||||||
create?: Prisma.XOR<Prisma.SportCreateWithoutCourtsInput, Prisma.SportUncheckedCreateWithoutCourtsInput>
|
create?: Prisma.XOR<Prisma.SportCreateWithoutCourtsInput, Prisma.SportUncheckedCreateWithoutCourtsInput>
|
||||||
connectOrCreate?: Prisma.SportCreateOrConnectWithoutCourtsInput
|
connectOrCreate?: Prisma.SportCreateOrConnectWithoutCourtsInput
|
||||||
|
|||||||
@@ -26,58 +26,76 @@ export type AggregateUser = {
|
|||||||
|
|
||||||
export type UserMinAggregateOutputType = {
|
export type UserMinAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
supabaseUserId: string | null
|
name: string | null
|
||||||
email: string | null
|
email: string | null
|
||||||
fullName: string | null
|
emailVerified: boolean | null
|
||||||
|
image: string | null
|
||||||
|
phone: string | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
|
role: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserMaxAggregateOutputType = {
|
export type UserMaxAggregateOutputType = {
|
||||||
id: string | null
|
id: string | null
|
||||||
supabaseUserId: string | null
|
name: string | null
|
||||||
email: string | null
|
email: string | null
|
||||||
fullName: string | null
|
emailVerified: boolean | null
|
||||||
|
image: string | null
|
||||||
|
phone: string | null
|
||||||
createdAt: Date | null
|
createdAt: Date | null
|
||||||
updatedAt: Date | null
|
updatedAt: Date | null
|
||||||
|
role: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCountAggregateOutputType = {
|
export type UserCountAggregateOutputType = {
|
||||||
id: number
|
id: number
|
||||||
supabaseUserId: number
|
name: number
|
||||||
email: number
|
email: number
|
||||||
fullName: number
|
emailVerified: number
|
||||||
|
image: number
|
||||||
|
phone: number
|
||||||
createdAt: number
|
createdAt: number
|
||||||
updatedAt: number
|
updatedAt: number
|
||||||
|
role: number
|
||||||
_all: number
|
_all: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export type UserMinAggregateInputType = {
|
export type UserMinAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
supabaseUserId?: true
|
name?: true
|
||||||
email?: true
|
email?: true
|
||||||
fullName?: true
|
emailVerified?: true
|
||||||
|
image?: true
|
||||||
|
phone?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
|
role?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserMaxAggregateInputType = {
|
export type UserMaxAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
supabaseUserId?: true
|
name?: true
|
||||||
email?: true
|
email?: true
|
||||||
fullName?: true
|
emailVerified?: true
|
||||||
|
image?: true
|
||||||
|
phone?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
|
role?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCountAggregateInputType = {
|
export type UserCountAggregateInputType = {
|
||||||
id?: true
|
id?: true
|
||||||
supabaseUserId?: true
|
name?: true
|
||||||
email?: true
|
email?: true
|
||||||
fullName?: true
|
emailVerified?: true
|
||||||
|
image?: true
|
||||||
|
phone?: true
|
||||||
createdAt?: true
|
createdAt?: true
|
||||||
updatedAt?: true
|
updatedAt?: true
|
||||||
|
role?: true
|
||||||
_all?: true
|
_all?: true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,11 +173,14 @@ export type UserGroupByArgs<ExtArgs extends runtime.Types.Extensions.InternalArg
|
|||||||
|
|
||||||
export type UserGroupByOutputType = {
|
export type UserGroupByOutputType = {
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified: boolean
|
||||||
|
image: string | null
|
||||||
|
phone: string | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
role: string
|
||||||
_count: UserCountAggregateOutputType | null
|
_count: UserCountAggregateOutputType | null
|
||||||
_min: UserMinAggregateOutputType | null
|
_min: UserMinAggregateOutputType | null
|
||||||
_max: UserMaxAggregateOutputType | null
|
_max: UserMaxAggregateOutputType | null
|
||||||
@@ -184,45 +205,63 @@ export type UserWhereInput = {
|
|||||||
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
OR?: Prisma.UserWhereInput[]
|
OR?: Prisma.UserWhereInput[]
|
||||||
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
id?: Prisma.UuidFilter<"User"> | string
|
id?: Prisma.StringFilter<"User"> | string
|
||||||
supabaseUserId?: Prisma.UuidFilter<"User"> | string
|
name?: Prisma.StringFilter<"User"> | string
|
||||||
email?: Prisma.StringFilter<"User"> | string
|
email?: Prisma.StringFilter<"User"> | string
|
||||||
fullName?: Prisma.StringFilter<"User"> | string
|
emailVerified?: Prisma.BoolFilter<"User"> | boolean
|
||||||
|
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
|
phone?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
|
role?: Prisma.StringFilter<"User"> | string
|
||||||
|
sessions?: Prisma.SessionListRelationFilter
|
||||||
|
accounts?: Prisma.AccountListRelationFilter
|
||||||
complexes?: Prisma.ComplexUserListRelationFilter
|
complexes?: Prisma.ComplexUserListRelationFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserOrderByWithRelationInput = {
|
export type UserOrderByWithRelationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
supabaseUserId?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
fullName?: Prisma.SortOrder
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
phone?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
|
role?: Prisma.SortOrder
|
||||||
|
sessions?: Prisma.SessionOrderByRelationAggregateInput
|
||||||
|
accounts?: Prisma.AccountOrderByRelationAggregateInput
|
||||||
complexes?: Prisma.ComplexUserOrderByRelationAggregateInput
|
complexes?: Prisma.ComplexUserOrderByRelationAggregateInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserWhereUniqueInput = Prisma.AtLeast<{
|
export type UserWhereUniqueInput = Prisma.AtLeast<{
|
||||||
id?: string
|
id?: string
|
||||||
supabaseUserId?: string
|
|
||||||
email?: string
|
email?: string
|
||||||
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
AND?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
OR?: Prisma.UserWhereInput[]
|
OR?: Prisma.UserWhereInput[]
|
||||||
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
NOT?: Prisma.UserWhereInput | Prisma.UserWhereInput[]
|
||||||
fullName?: Prisma.StringFilter<"User"> | string
|
name?: Prisma.StringFilter<"User"> | string
|
||||||
|
emailVerified?: Prisma.BoolFilter<"User"> | boolean
|
||||||
|
image?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
|
phone?: Prisma.StringNullableFilter<"User"> | string | null
|
||||||
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeFilter<"User"> | Date | string
|
||||||
|
role?: Prisma.StringFilter<"User"> | string
|
||||||
|
sessions?: Prisma.SessionListRelationFilter
|
||||||
|
accounts?: Prisma.AccountListRelationFilter
|
||||||
complexes?: Prisma.ComplexUserListRelationFilter
|
complexes?: Prisma.ComplexUserListRelationFilter
|
||||||
}, "id" | "supabaseUserId" | "email">
|
}, "id" | "email">
|
||||||
|
|
||||||
export type UserOrderByWithAggregationInput = {
|
export type UserOrderByWithAggregationInput = {
|
||||||
id?: Prisma.SortOrder
|
id?: Prisma.SortOrder
|
||||||
supabaseUserId?: Prisma.SortOrder
|
name?: Prisma.SortOrder
|
||||||
email?: Prisma.SortOrder
|
email?: Prisma.SortOrder
|
||||||
fullName?: Prisma.SortOrder
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
|
phone?: Prisma.SortOrderInput | Prisma.SortOrder
|
||||||
createdAt?: Prisma.SortOrder
|
createdAt?: Prisma.SortOrder
|
||||||
updatedAt?: Prisma.SortOrder
|
updatedAt?: Prisma.SortOrder
|
||||||
|
role?: Prisma.SortOrder
|
||||||
_count?: Prisma.UserCountOrderByAggregateInput
|
_count?: Prisma.UserCountOrderByAggregateInput
|
||||||
_max?: Prisma.UserMaxOrderByAggregateInput
|
_max?: Prisma.UserMaxOrderByAggregateInput
|
||||||
_min?: Prisma.UserMinOrderByAggregateInput
|
_min?: Prisma.UserMinOrderByAggregateInput
|
||||||
@@ -232,79 +271,147 @@ export type UserScalarWhereWithAggregatesInput = {
|
|||||||
AND?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
AND?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
||||||
OR?: Prisma.UserScalarWhereWithAggregatesInput[]
|
OR?: Prisma.UserScalarWhereWithAggregatesInput[]
|
||||||
NOT?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
NOT?: Prisma.UserScalarWhereWithAggregatesInput | Prisma.UserScalarWhereWithAggregatesInput[]
|
||||||
id?: Prisma.UuidWithAggregatesFilter<"User"> | string
|
id?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
supabaseUserId?: Prisma.UuidWithAggregatesFilter<"User"> | string
|
name?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
email?: Prisma.StringWithAggregatesFilter<"User"> | string
|
email?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
fullName?: Prisma.StringWithAggregatesFilter<"User"> | string
|
emailVerified?: Prisma.BoolWithAggregatesFilter<"User"> | boolean
|
||||||
|
image?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||||
|
phone?: Prisma.StringNullableWithAggregatesFilter<"User"> | string | null
|
||||||
createdAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
createdAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
||||||
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
updatedAt?: Prisma.DateTimeWithAggregatesFilter<"User"> | Date | string
|
||||||
|
role?: Prisma.StringWithAggregatesFilter<"User"> | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateInput = {
|
export type UserCreateInput = {
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateInput = {
|
export type UserUncheckedCreateInput = {
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateInput = {
|
export type UserUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateInput = {
|
export type UserUncheckedUpdateInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateManyInput = {
|
export type UserCreateManyInput = {
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUpdateManyMutationInput = {
|
export type UserUpdateManyMutationInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateManyInput = {
|
export type UserUncheckedUpdateManyInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCountOrderByAggregateInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrder
|
||||||
|
phone?: Prisma.SortOrder
|
||||||
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
|
role?: Prisma.SortOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserMaxOrderByAggregateInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrder
|
||||||
|
phone?: Prisma.SortOrder
|
||||||
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
|
role?: Prisma.SortOrder
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserMinOrderByAggregateInput = {
|
||||||
|
id?: Prisma.SortOrder
|
||||||
|
name?: Prisma.SortOrder
|
||||||
|
email?: Prisma.SortOrder
|
||||||
|
emailVerified?: Prisma.SortOrder
|
||||||
|
image?: Prisma.SortOrder
|
||||||
|
phone?: Prisma.SortOrder
|
||||||
|
createdAt?: Prisma.SortOrder
|
||||||
|
updatedAt?: Prisma.SortOrder
|
||||||
|
role?: Prisma.SortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserScalarRelationFilter = {
|
export type UserScalarRelationFilter = {
|
||||||
@@ -312,31 +419,48 @@ export type UserScalarRelationFilter = {
|
|||||||
isNot?: Prisma.UserWhereInput
|
isNot?: Prisma.UserWhereInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCountOrderByAggregateInput = {
|
export type StringFieldUpdateOperationsInput = {
|
||||||
id?: Prisma.SortOrder
|
set?: string
|
||||||
supabaseUserId?: Prisma.SortOrder
|
|
||||||
email?: Prisma.SortOrder
|
|
||||||
fullName?: Prisma.SortOrder
|
|
||||||
createdAt?: Prisma.SortOrder
|
|
||||||
updatedAt?: Prisma.SortOrder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserMaxOrderByAggregateInput = {
|
export type BoolFieldUpdateOperationsInput = {
|
||||||
id?: Prisma.SortOrder
|
set?: boolean
|
||||||
supabaseUserId?: Prisma.SortOrder
|
|
||||||
email?: Prisma.SortOrder
|
|
||||||
fullName?: Prisma.SortOrder
|
|
||||||
createdAt?: Prisma.SortOrder
|
|
||||||
updatedAt?: Prisma.SortOrder
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserMinOrderByAggregateInput = {
|
export type NullableStringFieldUpdateOperationsInput = {
|
||||||
id?: Prisma.SortOrder
|
set?: string | null
|
||||||
supabaseUserId?: Prisma.SortOrder
|
}
|
||||||
email?: Prisma.SortOrder
|
|
||||||
fullName?: Prisma.SortOrder
|
export type DateTimeFieldUpdateOperationsInput = {
|
||||||
createdAt?: Prisma.SortOrder
|
set?: Date | string
|
||||||
updatedAt?: Prisma.SortOrder
|
}
|
||||||
|
|
||||||
|
export type UserCreateNestedOneWithoutSessionsInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutSessionsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateOneRequiredWithoutSessionsNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutSessionsInput
|
||||||
|
upsert?: Prisma.UserUpsertWithoutSessionsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutSessionsInput, Prisma.UserUpdateWithoutSessionsInput>, Prisma.UserUncheckedUpdateWithoutSessionsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateNestedOneWithoutAccountsInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAccountsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateOneRequiredWithoutAccountsNestedInput = {
|
||||||
|
create?: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
connectOrCreate?: Prisma.UserCreateOrConnectWithoutAccountsInput
|
||||||
|
upsert?: Prisma.UserUpsertWithoutAccountsInput
|
||||||
|
connect?: Prisma.UserWhereUniqueInput
|
||||||
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutAccountsInput, Prisma.UserUpdateWithoutAccountsInput>, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateNestedOneWithoutComplexesInput = {
|
export type UserCreateNestedOneWithoutComplexesInput = {
|
||||||
@@ -353,22 +477,176 @@ export type UserUpdateOneRequiredWithoutComplexesNestedInput = {
|
|||||||
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutComplexesInput, Prisma.UserUpdateWithoutComplexesInput>, Prisma.UserUncheckedUpdateWithoutComplexesInput>
|
update?: Prisma.XOR<Prisma.XOR<Prisma.UserUpdateToOneWithWhereWithoutComplexesInput, Prisma.UserUpdateWithoutComplexesInput>, Prisma.UserUncheckedUpdateWithoutComplexesInput>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateWithoutComplexesInput = {
|
export type UserCreateWithoutSessionsInput = {
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedCreateWithoutSessionsInput = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateOrConnectWithoutSessionsInput = {
|
||||||
|
where: Prisma.UserWhereUniqueInput
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpsertWithoutSessionsInput = {
|
||||||
|
update: Prisma.XOR<Prisma.UserUpdateWithoutSessionsInput, Prisma.UserUncheckedUpdateWithoutSessionsInput>
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutSessionsInput, Prisma.UserUncheckedCreateWithoutSessionsInput>
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateToOneWithWhereWithoutSessionsInput = {
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.UserUpdateWithoutSessionsInput, Prisma.UserUncheckedUpdateWithoutSessionsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateWithoutSessionsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedUpdateWithoutSessionsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateWithoutAccountsInput = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedCreateWithoutAccountsInput = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateOrConnectWithoutAccountsInput = {
|
||||||
|
where: Prisma.UserWhereUniqueInput
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpsertWithoutAccountsInput = {
|
||||||
|
update: Prisma.XOR<Prisma.UserUpdateWithoutAccountsInput, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||||
|
create: Prisma.XOR<Prisma.UserCreateWithoutAccountsInput, Prisma.UserUncheckedCreateWithoutAccountsInput>
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateToOneWithWhereWithoutAccountsInput = {
|
||||||
|
where?: Prisma.UserWhereInput
|
||||||
|
data: Prisma.XOR<Prisma.UserUpdateWithoutAccountsInput, Prisma.UserUncheckedUpdateWithoutAccountsInput>
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUpdateWithoutAccountsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserUncheckedUpdateWithoutAccountsInput = {
|
||||||
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
complexes?: Prisma.ComplexUserUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UserCreateWithoutComplexesInput = {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
email: string
|
||||||
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
|
createdAt?: Date | string
|
||||||
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
sessions?: Prisma.SessionCreateNestedManyWithoutUserInput
|
||||||
|
accounts?: Prisma.AccountCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedCreateWithoutComplexesInput = {
|
export type UserUncheckedCreateWithoutComplexesInput = {
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified?: boolean
|
||||||
|
image?: string | null
|
||||||
|
phone?: string | null
|
||||||
createdAt?: Date | string
|
createdAt?: Date | string
|
||||||
updatedAt?: Date | string
|
updatedAt?: Date | string
|
||||||
|
role?: string
|
||||||
|
sessions?: Prisma.SessionUncheckedCreateNestedManyWithoutUserInput
|
||||||
|
accounts?: Prisma.AccountUncheckedCreateNestedManyWithoutUserInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCreateOrConnectWithoutComplexesInput = {
|
export type UserCreateOrConnectWithoutComplexesInput = {
|
||||||
@@ -389,20 +667,30 @@ export type UserUpdateToOneWithWhereWithoutComplexesInput = {
|
|||||||
|
|
||||||
export type UserUpdateWithoutComplexesInput = {
|
export type UserUpdateWithoutComplexesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
sessions?: Prisma.SessionUpdateManyWithoutUserNestedInput
|
||||||
|
accounts?: Prisma.AccountUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserUncheckedUpdateWithoutComplexesInput = {
|
export type UserUncheckedUpdateWithoutComplexesInput = {
|
||||||
id?: Prisma.StringFieldUpdateOperationsInput | string
|
id?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
supabaseUserId?: Prisma.StringFieldUpdateOperationsInput | string
|
name?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
email?: Prisma.StringFieldUpdateOperationsInput | string
|
email?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
fullName?: Prisma.StringFieldUpdateOperationsInput | string
|
emailVerified?: Prisma.BoolFieldUpdateOperationsInput | boolean
|
||||||
|
image?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
|
phone?: Prisma.NullableStringFieldUpdateOperationsInput | string | null
|
||||||
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
createdAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
updatedAt?: Prisma.DateTimeFieldUpdateOperationsInput | Date | string
|
||||||
|
role?: Prisma.StringFieldUpdateOperationsInput | string
|
||||||
|
sessions?: Prisma.SessionUncheckedUpdateManyWithoutUserNestedInput
|
||||||
|
accounts?: Prisma.AccountUncheckedUpdateManyWithoutUserNestedInput
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -411,10 +699,14 @@ export type UserUncheckedUpdateWithoutComplexesInput = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export type UserCountOutputType = {
|
export type UserCountOutputType = {
|
||||||
|
sessions: number
|
||||||
|
accounts: number
|
||||||
complexes: number
|
complexes: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type UserCountOutputTypeSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
sessions?: boolean | UserCountOutputTypeCountSessionsArgs
|
||||||
|
accounts?: boolean | UserCountOutputTypeCountAccountsArgs
|
||||||
complexes?: boolean | UserCountOutputTypeCountComplexesArgs
|
complexes?: boolean | UserCountOutputTypeCountComplexesArgs
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,6 +720,20 @@ export type UserCountOutputTypeDefaultArgs<ExtArgs extends runtime.Types.Extensi
|
|||||||
select?: Prisma.UserCountOutputTypeSelect<ExtArgs> | null
|
select?: Prisma.UserCountOutputTypeSelect<ExtArgs> | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCountOutputType without action
|
||||||
|
*/
|
||||||
|
export type UserCountOutputTypeCountSessionsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
where?: Prisma.SessionWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UserCountOutputType without action
|
||||||
|
*/
|
||||||
|
export type UserCountOutputTypeCountAccountsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
where?: Prisma.AccountWhereInput
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserCountOutputType without action
|
* UserCountOutputType without action
|
||||||
*/
|
*/
|
||||||
@@ -438,44 +744,60 @@ export type UserCountOutputTypeCountComplexesArgs<ExtArgs extends runtime.Types.
|
|||||||
|
|
||||||
export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type UserSelect<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
fullName?: boolean
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
|
phone?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
|
role?: boolean
|
||||||
|
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||||
|
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||||
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
|
|
||||||
export type UserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type UserSelectCreateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
fullName?: boolean
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
|
phone?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
|
role?: boolean
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
|
|
||||||
export type UserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
export type UserSelectUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetSelect<{
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
fullName?: boolean
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
|
phone?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
|
role?: boolean
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
|
|
||||||
export type UserSelectScalar = {
|
export type UserSelectScalar = {
|
||||||
id?: boolean
|
id?: boolean
|
||||||
supabaseUserId?: boolean
|
name?: boolean
|
||||||
email?: boolean
|
email?: boolean
|
||||||
fullName?: boolean
|
emailVerified?: boolean
|
||||||
|
image?: boolean
|
||||||
|
phone?: boolean
|
||||||
createdAt?: boolean
|
createdAt?: boolean
|
||||||
updatedAt?: boolean
|
updatedAt?: boolean
|
||||||
|
role?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "supabaseUserId" | "email" | "fullName" | "createdAt" | "updatedAt", ExtArgs["result"]["user"]>
|
export type UserOmit<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = runtime.Types.Extensions.GetOmit<"id" | "name" | "email" | "emailVerified" | "image" | "phone" | "createdAt" | "updatedAt" | "role", ExtArgs["result"]["user"]>
|
||||||
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type UserInclude<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
sessions?: boolean | Prisma.User$sessionsArgs<ExtArgs>
|
||||||
|
accounts?: boolean | Prisma.User$accountsArgs<ExtArgs>
|
||||||
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
complexes?: boolean | Prisma.User$complexesArgs<ExtArgs>
|
||||||
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
_count?: boolean | Prisma.UserCountOutputTypeDefaultArgs<ExtArgs>
|
||||||
}
|
}
|
||||||
@@ -485,15 +807,20 @@ export type UserIncludeUpdateManyAndReturn<ExtArgs extends runtime.Types.Extensi
|
|||||||
export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
export type $UserPayload<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
name: "User"
|
name: "User"
|
||||||
objects: {
|
objects: {
|
||||||
|
sessions: Prisma.$SessionPayload<ExtArgs>[]
|
||||||
|
accounts: Prisma.$AccountPayload<ExtArgs>[]
|
||||||
complexes: Prisma.$ComplexUserPayload<ExtArgs>[]
|
complexes: Prisma.$ComplexUserPayload<ExtArgs>[]
|
||||||
}
|
}
|
||||||
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
scalars: runtime.Types.Extensions.GetPayloadResult<{
|
||||||
id: string
|
id: string
|
||||||
supabaseUserId: string
|
name: string
|
||||||
email: string
|
email: string
|
||||||
fullName: string
|
emailVerified: boolean
|
||||||
|
image: string | null
|
||||||
|
phone: string | null
|
||||||
createdAt: Date
|
createdAt: Date
|
||||||
updatedAt: Date
|
updatedAt: Date
|
||||||
|
role: string
|
||||||
}, ExtArgs["result"]["user"]>
|
}, ExtArgs["result"]["user"]>
|
||||||
composites: {}
|
composites: {}
|
||||||
}
|
}
|
||||||
@@ -888,6 +1215,8 @@ readonly fields: UserFieldRefs;
|
|||||||
*/
|
*/
|
||||||
export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs, GlobalOmitOptions = {}> extends Prisma.PrismaPromise<T> {
|
||||||
readonly [Symbol.toStringTag]: "PrismaPromise"
|
readonly [Symbol.toStringTag]: "PrismaPromise"
|
||||||
|
sessions<T extends Prisma.User$sessionsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$sessionsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$SessionPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
|
accounts<T extends Prisma.User$accountsArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$accountsArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$AccountPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
complexes<T extends Prisma.User$complexesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$complexesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
complexes<T extends Prisma.User$complexesArgs<ExtArgs> = {}>(args?: Prisma.Subset<T, Prisma.User$complexesArgs<ExtArgs>>): Prisma.PrismaPromise<runtime.Types.Result.GetResult<Prisma.$ComplexUserPayload<ExtArgs>, T, "findMany", GlobalOmitOptions> | Null>
|
||||||
/**
|
/**
|
||||||
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
* Attaches callbacks for the resolution and/or rejection of the Promise.
|
||||||
@@ -919,11 +1248,14 @@ export interface Prisma__UserClient<T, Null = never, ExtArgs extends runtime.Typ
|
|||||||
*/
|
*/
|
||||||
export interface UserFieldRefs {
|
export interface UserFieldRefs {
|
||||||
readonly id: Prisma.FieldRef<"User", 'String'>
|
readonly id: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly supabaseUserId: Prisma.FieldRef<"User", 'String'>
|
readonly name: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly email: Prisma.FieldRef<"User", 'String'>
|
readonly email: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly fullName: Prisma.FieldRef<"User", 'String'>
|
readonly emailVerified: Prisma.FieldRef<"User", 'Boolean'>
|
||||||
|
readonly image: Prisma.FieldRef<"User", 'String'>
|
||||||
|
readonly phone: Prisma.FieldRef<"User", 'String'>
|
||||||
readonly createdAt: Prisma.FieldRef<"User", 'DateTime'>
|
readonly createdAt: Prisma.FieldRef<"User", 'DateTime'>
|
||||||
readonly updatedAt: Prisma.FieldRef<"User", 'DateTime'>
|
readonly updatedAt: Prisma.FieldRef<"User", 'DateTime'>
|
||||||
|
readonly role: Prisma.FieldRef<"User", 'String'>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1316,6 +1648,54 @@ export type UserDeleteManyArgs<ExtArgs extends runtime.Types.Extensions.Internal
|
|||||||
limit?: number
|
limit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User.sessions
|
||||||
|
*/
|
||||||
|
export type User$sessionsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the Session
|
||||||
|
*/
|
||||||
|
select?: Prisma.SessionSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the Session
|
||||||
|
*/
|
||||||
|
omit?: Prisma.SessionOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.SessionInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.SessionWhereInput
|
||||||
|
orderBy?: Prisma.SessionOrderByWithRelationInput | Prisma.SessionOrderByWithRelationInput[]
|
||||||
|
cursor?: Prisma.SessionWhereUniqueInput
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
distinct?: Prisma.SessionScalarFieldEnum | Prisma.SessionScalarFieldEnum[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User.accounts
|
||||||
|
*/
|
||||||
|
export type User$accountsArgs<ExtArgs extends runtime.Types.Extensions.InternalArgs = runtime.Types.Extensions.DefaultArgs> = {
|
||||||
|
/**
|
||||||
|
* Select specific fields to fetch from the Account
|
||||||
|
*/
|
||||||
|
select?: Prisma.AccountSelect<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Omit specific fields from the Account
|
||||||
|
*/
|
||||||
|
omit?: Prisma.AccountOmit<ExtArgs> | null
|
||||||
|
/**
|
||||||
|
* Choose, which related nodes to fetch as well
|
||||||
|
*/
|
||||||
|
include?: Prisma.AccountInclude<ExtArgs> | null
|
||||||
|
where?: Prisma.AccountWhereInput
|
||||||
|
orderBy?: Prisma.AccountOrderByWithRelationInput | Prisma.AccountOrderByWithRelationInput[]
|
||||||
|
cursor?: Prisma.AccountWhereUniqueInput
|
||||||
|
take?: number
|
||||||
|
skip?: number
|
||||||
|
distinct?: Prisma.AccountScalarFieldEnum | Prisma.AccountScalarFieldEnum[]
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* User.complexes
|
* User.complexes
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { createApp } from '@/app'
|
import { createApp } from '@/app';
|
||||||
import { registerRoutes } from '@/register-routes'
|
import { registerRoutes } from '@/register-routes';
|
||||||
|
|
||||||
const app = createApp()
|
const app = createApp();
|
||||||
registerRoutes(app)
|
registerRoutes(app);
|
||||||
|
|
||||||
export type AppType = typeof app
|
export type AppType = typeof app;
|
||||||
export default app
|
export default app;
|
||||||
|
|||||||
123
apps/backend/src/lib/auth.ts
Normal file
123
apps/backend/src/lib/auth.ts
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import { wrapLayout } from '@/emails/booking-confirmation';
|
||||||
|
import { dash } from '@better-auth/infra';
|
||||||
|
import { betterAuth } from 'better-auth';
|
||||||
|
import { prismaAdapter } from 'better-auth/adapters/prisma';
|
||||||
|
import { openAPI } from 'better-auth/plugins';
|
||||||
|
import { sendMail } from './mailer';
|
||||||
|
import { db } from './prisma';
|
||||||
|
|
||||||
|
export const auth = betterAuth({
|
||||||
|
database: prismaAdapter(db, {
|
||||||
|
provider: 'postgresql',
|
||||||
|
}),
|
||||||
|
secret: process.env.BETTER_AUTH_SECRET,
|
||||||
|
baseURL: process.env.BETTER_AUTH_URL,
|
||||||
|
session: {
|
||||||
|
cookieCache: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emailAndPassword: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
additionalFields: {
|
||||||
|
phone: {
|
||||||
|
type: 'string',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emailVerification: {
|
||||||
|
sendOnSignUp: true,
|
||||||
|
autoSignInAfterVerification: true,
|
||||||
|
sendVerificationEmail: async ({ user, url }) => {
|
||||||
|
const verificationUrl = new URL(url);
|
||||||
|
const appUrl = process.env.APP_BASE_URL ?? 'http://localhost:5173';
|
||||||
|
verificationUrl.searchParams.set('callbackURL', appUrl);
|
||||||
|
|
||||||
|
const content = `
|
||||||
|
<tr>
|
||||||
|
<td style="padding:32px 32px 24px;">
|
||||||
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="top">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0" style="display:inline-block;background-color:#f4f4f5;border-radius:999px;padding:4px 12px;">
|
||||||
|
<tr>
|
||||||
|
<td style="font-size:11px;font-weight:700;letter-spacing:0.14em;color:#71717a;text-transform:uppercase;line-height:1.25rem;">
|
||||||
|
Verificación de email
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<h1 style="margin:16px 0 0;font-size:24px;font-weight:700;color:#111827;letter-spacing:-0.025em;">
|
||||||
|
Verificá tu dirección de correo electrónico
|
||||||
|
</h1>
|
||||||
|
</td>
|
||||||
|
<td valign="top" align="right" style="white-space:nowrap;">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td valign="middle" style="padding-right:8px;">
|
||||||
|
<img src="${appUrl}/playzer-favicon-512-transparent.png" alt="Playzer" width="32" height="32" style="display:block;" />
|
||||||
|
</td>
|
||||||
|
<td valign="middle">
|
||||||
|
<span style="font-size:18px;font-weight:700;color:#475569;letter-spacing:-0.025em;">Playzer</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 24px;">
|
||||||
|
<p style="margin:0;font-size:15px;color:#374151;line-height:1.6;">
|
||||||
|
Hacé click en el botón de abajo para verificar tu dirección de correo electrónico y empezar a usar Playzer.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 32px;">
|
||||||
|
<table role="presentation" cellpadding="0" cellspacing="0">
|
||||||
|
<tr>
|
||||||
|
<td style="background-color:#059669;border-radius:12px;text-align:center;">
|
||||||
|
<a href="${verificationUrl.toString()}" style="display:block;padding:14px 32px;font-size:15px;font-weight:600;color:#ffffff;text-decoration:none;line-height:1.25rem;">Verificar email</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td style="padding:0 32px 32px;">
|
||||||
|
<p style="margin:0;font-size:13px;color:#9ca3af;text-align:center;line-height:1.5;">
|
||||||
|
Si no creaste una cuenta en Playzer, ignorá este mensaje.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
|
||||||
|
await sendMail({
|
||||||
|
to: user.email,
|
||||||
|
subject: 'Verificá tu email en Playzer',
|
||||||
|
html: wrapLayout(content),
|
||||||
|
text: `Hacé click para verificar tu email: ${verificationUrl.toString()}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
socialProviders: {
|
||||||
|
google: {
|
||||||
|
clientId: process.env.GOOGLE_CLIENT_ID!,
|
||||||
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
trustedOrigins: [
|
||||||
|
process.env.APP_BASE_URL ?? 'http://localhost:5173',
|
||||||
|
process.env.BETTER_AUTH_URL ?? 'http://localhost:3000',
|
||||||
|
],
|
||||||
|
plugins: [openAPI(), dash()],
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Session = typeof auth.$Infer.Session.session;
|
||||||
|
export type User = typeof auth.$Infer.Session.user;
|
||||||
17
apps/backend/src/lib/avatar.ts
Normal file
17
apps/backend/src/lib/avatar.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { createHash } from 'node:crypto';
|
||||||
|
|
||||||
|
function getGravatarUrl(email: string): string {
|
||||||
|
const hash = createHash('md5').update(email.trim().toLowerCase()).digest('hex');
|
||||||
|
return `https://www.gravatar.com/avatar/${hash}?d=identicon`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resolveAvatarUrl(input: {
|
||||||
|
email: string;
|
||||||
|
image?: string | null;
|
||||||
|
}): string {
|
||||||
|
if (input.image) {
|
||||||
|
return input.image;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getGravatarUrl(input.email);
|
||||||
|
}
|
||||||
58
apps/backend/src/lib/errors.ts
Normal file
58
apps/backend/src/lib/errors.ts
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// lib/errors.ts
|
||||||
|
export type ValidationIssue = {
|
||||||
|
path: string;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AppError =
|
||||||
|
| {
|
||||||
|
type: 'validation';
|
||||||
|
message: string;
|
||||||
|
issues?: ValidationIssue[];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'not_found';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'conflict';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'unauthorized';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'forbidden';
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'unexpected';
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Errors = {
|
||||||
|
validation(message: string, issues?: ValidationIssue[]): AppError {
|
||||||
|
return { type: 'validation', message, issues };
|
||||||
|
},
|
||||||
|
|
||||||
|
notFound(message = 'Resource not found'): AppError {
|
||||||
|
return { type: 'not_found', message };
|
||||||
|
},
|
||||||
|
|
||||||
|
conflict(message: string): AppError {
|
||||||
|
return { type: 'conflict', message };
|
||||||
|
},
|
||||||
|
|
||||||
|
unauthorized(message = 'Unauthorized'): AppError {
|
||||||
|
return { type: 'unauthorized', message };
|
||||||
|
},
|
||||||
|
|
||||||
|
forbidden(message = 'Forbidden'): AppError {
|
||||||
|
return { type: 'forbidden', message };
|
||||||
|
},
|
||||||
|
|
||||||
|
unexpected(message = 'Unexpected error'): AppError {
|
||||||
|
return { type: 'unexpected', message };
|
||||||
|
},
|
||||||
|
};
|
||||||
20
apps/backend/src/lib/http/error-handler.ts
Normal file
20
apps/backend/src/lib/http/error-handler.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// http/error-handler.ts
|
||||||
|
import type { MiddlewareHandler } from 'hono';
|
||||||
|
import { logger } from '../logger';
|
||||||
|
import { unexpectedProblem } from './problem-builders';
|
||||||
|
|
||||||
|
export const errorHandler: MiddlewareHandler = async (c, next) => {
|
||||||
|
try {
|
||||||
|
await next();
|
||||||
|
} catch (error) {
|
||||||
|
const requestId = c.get('requestId') as string | undefined;
|
||||||
|
const instance = requestId ? `/requests/${requestId}` : undefined;
|
||||||
|
|
||||||
|
logger.error({
|
||||||
|
requestId,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
|
||||||
|
return c.json(unexpectedProblem({ instance }), 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
20
apps/backend/src/lib/http/handle-result.ts
Normal file
20
apps/backend/src/lib/http/handle-result.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type { Result } from '@/lib/result';
|
||||||
|
import type { Context } from 'hono';
|
||||||
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||||
|
import { mapAppErrorToProblem } from './problem-mapper';
|
||||||
|
|
||||||
|
export function handleResult<T>(
|
||||||
|
c: Context,
|
||||||
|
result: Result<T>,
|
||||||
|
successStatus: ContentfulStatusCode = 200
|
||||||
|
) {
|
||||||
|
if (!result.ok) {
|
||||||
|
const requestId = c.get('requestId') as string | undefined;
|
||||||
|
const instance = requestId ? `/requests/${requestId}` : undefined;
|
||||||
|
const problem = mapAppErrorToProblem(result.error, instance);
|
||||||
|
|
||||||
|
return c.json(problem, problem.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json(result.value, successStatus);
|
||||||
|
}
|
||||||
29
apps/backend/src/lib/http/problem-builders.ts
Normal file
29
apps/backend/src/lib/http/problem-builders.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// http/problem-builders.ts
|
||||||
|
import type { ProblemDetails } from './problem-details';
|
||||||
|
|
||||||
|
export function validationProblem(params: {
|
||||||
|
detail?: string;
|
||||||
|
instance?: string;
|
||||||
|
errors?: Record<string, string[]>;
|
||||||
|
}): ProblemDetails {
|
||||||
|
return {
|
||||||
|
type: 'https://api.myapp.dev/problems/validation',
|
||||||
|
title: 'Bad Request',
|
||||||
|
status: 400,
|
||||||
|
detail: params.detail ?? 'Invalid request data',
|
||||||
|
instance: params.instance,
|
||||||
|
errors: params.errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unexpectedProblem(params?: {
|
||||||
|
instance?: string;
|
||||||
|
}): ProblemDetails {
|
||||||
|
return {
|
||||||
|
type: 'https://api.myapp.dev/problems/unexpected',
|
||||||
|
title: 'Internal Server Error',
|
||||||
|
status: 500,
|
||||||
|
detail: 'Internal server error',
|
||||||
|
instance: params?.instance,
|
||||||
|
};
|
||||||
|
}
|
||||||
10
apps/backend/src/lib/http/problem-details.ts
Normal file
10
apps/backend/src/lib/http/problem-details.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||||
|
|
||||||
|
export type ProblemDetails = {
|
||||||
|
type: string;
|
||||||
|
title: string;
|
||||||
|
status: ContentfulStatusCode;
|
||||||
|
detail?: string;
|
||||||
|
instance?: string;
|
||||||
|
errors?: Record<string, string[]>;
|
||||||
|
};
|
||||||
51
apps/backend/src/lib/http/problem-mapper.ts
Normal file
51
apps/backend/src/lib/http/problem-mapper.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import type { AppError } from '@/lib/errors';
|
||||||
|
import type { ContentfulStatusCode } from 'hono/utils/http-status';
|
||||||
|
import type { ProblemDetails } from './problem-details';
|
||||||
|
|
||||||
|
function statusFromError(error: AppError): ContentfulStatusCode {
|
||||||
|
switch (error.type) {
|
||||||
|
case 'validation':
|
||||||
|
return 400;
|
||||||
|
case 'unauthorized':
|
||||||
|
return 401;
|
||||||
|
case 'forbidden':
|
||||||
|
return 403;
|
||||||
|
case 'not_found':
|
||||||
|
return 404;
|
||||||
|
case 'conflict':
|
||||||
|
return 409;
|
||||||
|
case 'unexpected':
|
||||||
|
return 500;
|
||||||
|
default:
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function titleFromStatus(status: ContentfulStatusCode): string {
|
||||||
|
switch (status) {
|
||||||
|
case 400:
|
||||||
|
return 'Bad Request';
|
||||||
|
case 401:
|
||||||
|
return 'Unauthorized';
|
||||||
|
case 403:
|
||||||
|
return 'Forbidden';
|
||||||
|
case 404:
|
||||||
|
return 'Not Found';
|
||||||
|
case 409:
|
||||||
|
return 'Conflict';
|
||||||
|
default:
|
||||||
|
return 'Internal Server Error';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function mapAppErrorToProblem(error: AppError, instance?: string): ProblemDetails {
|
||||||
|
const status = statusFromError(error);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: `https://api.myapp.dev/problems/${error.type}`,
|
||||||
|
title: titleFromStatus(status),
|
||||||
|
status,
|
||||||
|
detail: status === 500 ? 'Internal server error' : error.message,
|
||||||
|
instance,
|
||||||
|
};
|
||||||
|
}
|
||||||
16
apps/backend/src/lib/http/request-id.ts
Normal file
16
apps/backend/src/lib/http/request-id.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { MiddlewareHandler } from 'hono';
|
||||||
|
|
||||||
|
type Env = {
|
||||||
|
Variables: {
|
||||||
|
requestId: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const requestIdMiddleware: MiddlewareHandler<Env> = async (c, next) => {
|
||||||
|
const requestId = crypto.randomUUID();
|
||||||
|
|
||||||
|
c.set('requestId', requestId);
|
||||||
|
c.header('X-Request-Id', requestId);
|
||||||
|
|
||||||
|
await next();
|
||||||
|
};
|
||||||
48
apps/backend/src/lib/http/validate.ts
Normal file
48
apps/backend/src/lib/http/validate.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { zValidator } from '@hono/zod-validator';
|
||||||
|
import type { ZodSchema } from 'zod';
|
||||||
|
import { validationProblem } from './problem-builders';
|
||||||
|
import { zodIssuesToRecord } from './zod-issues';
|
||||||
|
|
||||||
|
type ValidationTarget = 'json' | 'query' | 'param' | 'header' | 'form';
|
||||||
|
|
||||||
|
function makeValidator<TSchema extends ZodSchema>(target: ValidationTarget, schema: TSchema) {
|
||||||
|
return zValidator(target, schema, (result, c) => {
|
||||||
|
if (result.success) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestId = c.get('requestId') as string | undefined;
|
||||||
|
const instance = requestId ? `/requests/${requestId}` : undefined;
|
||||||
|
|
||||||
|
return c.json(
|
||||||
|
validationProblem({
|
||||||
|
detail: `Invalid ${target} data`,
|
||||||
|
instance,
|
||||||
|
errors: zodIssuesToRecord(result.error.issues),
|
||||||
|
}),
|
||||||
|
400
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validate = {
|
||||||
|
json<TSchema extends ZodSchema>(schema: TSchema) {
|
||||||
|
return makeValidator('json', schema);
|
||||||
|
},
|
||||||
|
|
||||||
|
query<TSchema extends ZodSchema>(schema: TSchema) {
|
||||||
|
return makeValidator('query', schema);
|
||||||
|
},
|
||||||
|
|
||||||
|
param<TSchema extends ZodSchema>(schema: TSchema) {
|
||||||
|
return makeValidator('param', schema);
|
||||||
|
},
|
||||||
|
|
||||||
|
header<TSchema extends ZodSchema>(schema: TSchema) {
|
||||||
|
return makeValidator('header', schema);
|
||||||
|
},
|
||||||
|
|
||||||
|
form<TSchema extends ZodSchema>(schema: TSchema) {
|
||||||
|
return makeValidator('form', schema);
|
||||||
|
},
|
||||||
|
};
|
||||||
16
apps/backend/src/lib/http/zod-issues.ts
Normal file
16
apps/backend/src/lib/http/zod-issues.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
type IssueLike = {
|
||||||
|
path: PropertyKey[];
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function zodIssuesToRecord(issues: IssueLike[]): Record<string, string[]> {
|
||||||
|
const out: Record<string, string[]> = {};
|
||||||
|
|
||||||
|
for (const issue of issues) {
|
||||||
|
const key = issue.path.length ? issue.path.join('.') : 'root';
|
||||||
|
out[key] ??= [];
|
||||||
|
out[key].push(issue.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import pino from 'pino'
|
import pino from 'pino';
|
||||||
|
|
||||||
const isProd = Bun.env.NODE_ENV === 'production'
|
const isProd = Bun.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
export const logger = pino({
|
export const logger = pino({
|
||||||
level: Bun.env.LOG_LEVEL ?? (isProd ? 'info' : 'debug'),
|
level: Bun.env.LOG_LEVEL ?? (isProd ? 'info' : 'debug'),
|
||||||
@@ -15,4 +15,4 @@ export const logger = pino({
|
|||||||
ignore: 'pid,hostname',
|
ignore: 'pid,hostname',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -1,44 +1,39 @@
|
|||||||
import nodemailer from 'nodemailer'
|
import { MailtrapClientConfig, MailtrapTransport } from 'mailtrap';
|
||||||
|
import nodemailer from 'nodemailer';
|
||||||
|
|
||||||
let cachedTransporter: ReturnType<typeof nodemailer.createTransport> | null = null
|
const TOKEN = Bun.env.MAILTRAP_API_TOKEN;
|
||||||
|
|
||||||
|
let cachedTransporter: ReturnType<typeof nodemailer.createTransport> | null = null;
|
||||||
|
|
||||||
function getTransporter() {
|
function getTransporter() {
|
||||||
if (cachedTransporter) return cachedTransporter
|
if (cachedTransporter) return cachedTransporter;
|
||||||
|
|
||||||
const smtpHost = Bun.env.SMTP_HOST
|
if (!TOKEN) {
|
||||||
const smtpPort = Number(Bun.env.SMTP_PORT ?? 587)
|
throw new Error('Missing MAILTRAP_API_TOKEN env var.');
|
||||||
const smtpUser = Bun.env.SMTP_USER
|
|
||||||
const smtpPass = Bun.env.SMTP_PASS
|
|
||||||
|
|
||||||
if (!smtpHost || !smtpUser || !smtpPass) {
|
|
||||||
throw new Error(
|
|
||||||
'Missing SMTP env vars. Set SMTP_HOST, SMTP_PORT, SMTP_USER and SMTP_PASS.',
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cachedTransporter = nodemailer.createTransport({
|
const config: MailtrapClientConfig = {
|
||||||
host: smtpHost,
|
token: TOKEN,
|
||||||
port: smtpPort,
|
sandbox: Bun.env.MAIL_SANDBOX === 'true',
|
||||||
secure: smtpPort === 465,
|
testInboxId: Bun.env.MAIL_INBOX_ID ? Number(Bun.env.MAIL_INBOX_ID) : undefined,
|
||||||
auth: {
|
};
|
||||||
user: smtpUser,
|
|
||||||
pass: smtpPass,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return cachedTransporter
|
console.log(JSON.stringify(config, null, 2));
|
||||||
|
|
||||||
|
cachedTransporter = nodemailer.createTransport(MailtrapTransport(config));
|
||||||
|
return cachedTransporter;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendMail(input: {
|
export async function sendMail(input: {
|
||||||
to: string
|
to: string;
|
||||||
subject: string
|
subject: string;
|
||||||
html: string
|
html: string;
|
||||||
text: string
|
text: string;
|
||||||
}) {
|
}) {
|
||||||
const smtpFrom = Bun.env.SMTP_FROM
|
const smtpFrom = Bun.env.SMTP_FROM;
|
||||||
|
|
||||||
if (!smtpFrom) {
|
if (!smtpFrom) {
|
||||||
throw new Error('Missing SMTP_FROM env var.')
|
throw new Error('Missing SMTP_FROM env var.');
|
||||||
}
|
}
|
||||||
|
|
||||||
await getTransporter().sendMail({
|
await getTransporter().sendMail({
|
||||||
@@ -47,5 +42,5 @@ export async function sendMail(input: {
|
|||||||
subject: input.subject,
|
subject: input.subject,
|
||||||
html: input.html,
|
html: input.html,
|
||||||
text: input.text,
|
text: input.text,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,50 @@
|
|||||||
import { PrismaPg } from '@prisma/adapter-pg'
|
import { PrismaClient } from '@/generated/prisma/client';
|
||||||
import { PrismaClient } from '@/generated/prisma/client'
|
import { PrismaPg } from '@prisma/adapter-pg';
|
||||||
|
|
||||||
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient }
|
const globalForPrisma = globalThis as unknown as { prisma?: PrismaClient };
|
||||||
|
|
||||||
function hasExpectedDelegates(client: PrismaClient) {
|
function hasExpectedDelegates(client: PrismaClient) {
|
||||||
const prismaClient = client as unknown as {
|
const prismaClient = client as unknown as {
|
||||||
court?: { findMany?: unknown }
|
court?: { findMany?: unknown };
|
||||||
sport?: { findMany?: unknown }
|
sport?: { findMany?: unknown };
|
||||||
}
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
typeof prismaClient.court?.findMany === 'function' &&
|
typeof prismaClient.court?.findMany === 'function' &&
|
||||||
typeof prismaClient.sport?.findMany === 'function'
|
typeof prismaClient.sport?.findMany === 'function'
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPrismaClient() {
|
function buildPrismaClient() {
|
||||||
const databaseUrl = Bun.env.DATABASE_URL
|
const databaseUrl = Bun.env.DATABASE_URL;
|
||||||
|
|
||||||
if (!databaseUrl) {
|
if (!databaseUrl) {
|
||||||
throw new Error('Missing DATABASE_URL in backend environment.')
|
throw new Error('Missing DATABASE_URL in backend environment.');
|
||||||
}
|
}
|
||||||
|
|
||||||
const adapter = new PrismaPg({
|
const adapter = new PrismaPg({
|
||||||
connectionString: databaseUrl,
|
connectionString: databaseUrl,
|
||||||
})
|
});
|
||||||
|
|
||||||
return new PrismaClient({ adapter })
|
return new PrismaClient({ adapter });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPrismaClient() {
|
export function getPrismaClient() {
|
||||||
if (globalForPrisma.prisma && hasExpectedDelegates(globalForPrisma.prisma)) {
|
if (globalForPrisma.prisma && hasExpectedDelegates(globalForPrisma.prisma)) {
|
||||||
return globalForPrisma.prisma
|
return globalForPrisma.prisma;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalForPrisma.prisma && !hasExpectedDelegates(globalForPrisma.prisma)) {
|
if (globalForPrisma.prisma && !hasExpectedDelegates(globalForPrisma.prisma)) {
|
||||||
void globalForPrisma.prisma.$disconnect()
|
void globalForPrisma.prisma.$disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const prisma = buildPrismaClient()
|
const prisma = buildPrismaClient();
|
||||||
|
|
||||||
if (Bun.env.NODE_ENV !== 'production') {
|
if (Bun.env.NODE_ENV !== 'production') {
|
||||||
globalForPrisma.prisma = prisma
|
globalForPrisma.prisma = prisma;
|
||||||
}
|
}
|
||||||
|
|
||||||
return prisma
|
return prisma;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const db = getPrismaClient()
|
export const db = getPrismaClient();
|
||||||
|
|||||||
12
apps/backend/src/lib/result.ts
Normal file
12
apps/backend/src/lib/result.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// lib/result.ts
|
||||||
|
import type { AppError } from '@/lib/errors';
|
||||||
|
|
||||||
|
export type Result<T, E = AppError> = { ok: true; value: T } | { ok: false; error: E };
|
||||||
|
|
||||||
|
export function ok<T>(value: T): Result<T> {
|
||||||
|
return { ok: true, value };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function err<E>(error: E): Result<never, E> {
|
||||||
|
return { ok: false, error };
|
||||||
|
}
|
||||||
84
apps/backend/src/lib/sse.ts
Normal file
84
apps/backend/src/lib/sse.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import { Hono } from 'hono';
|
||||||
|
|
||||||
|
type EventCallback = (data: string) => void;
|
||||||
|
|
||||||
|
class SSEManager {
|
||||||
|
private channels: Map<string, Set<EventCallback>> = new Map();
|
||||||
|
|
||||||
|
subscribe(channel: string, callback: EventCallback): () => void {
|
||||||
|
if (!this.channels.has(channel)) {
|
||||||
|
this.channels.set(channel, new Set());
|
||||||
|
}
|
||||||
|
this.channels.get(channel)!.add(callback);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const subs = this.channels.get(channel);
|
||||||
|
if (subs) {
|
||||||
|
subs.delete(callback);
|
||||||
|
if (subs.size === 0) {
|
||||||
|
this.channels.delete(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
emit(channel: string, data: string): void {
|
||||||
|
const subs = this.channels.get(channel);
|
||||||
|
if (subs) {
|
||||||
|
for (const callback of subs) {
|
||||||
|
callback(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sseManager = new SSEManager();
|
||||||
|
|
||||||
|
export function registerEventsRoutes(app: Hono<AppEnv>) {
|
||||||
|
app.get('/api/events/:channel', async (c) => {
|
||||||
|
const channel = c.req.param('channel');
|
||||||
|
|
||||||
|
const stream = new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
|
||||||
|
const send = (data: string) => {
|
||||||
|
try {
|
||||||
|
controller.enqueue(encoder.encode(`data: ${data}\n\n`));
|
||||||
|
} catch {
|
||||||
|
// Controller closed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
send('connected');
|
||||||
|
|
||||||
|
const unsubscribe = sseManager.subscribe(channel, send);
|
||||||
|
|
||||||
|
const keepAliveInterval = setInterval(() => {
|
||||||
|
send('ping');
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
c.req.raw.signal.addEventListener('abort', () => {
|
||||||
|
console.log(`[SSE] Connection closed for channel: ${channel}`);
|
||||||
|
clearInterval(keepAliveInterval);
|
||||||
|
unsubscribe();
|
||||||
|
try {
|
||||||
|
controller.close();
|
||||||
|
} catch {
|
||||||
|
// Already closed
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(stream, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/event-stream',
|
||||||
|
'Cache-Control': 'no-cache',
|
||||||
|
Connection: 'keep-alive',
|
||||||
|
'X-Accel-Buffering': 'no',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { createClient } from '@supabase/supabase-js'
|
|
||||||
|
|
||||||
let cachedClient: ReturnType<typeof createClient> | null = null
|
|
||||||
|
|
||||||
export function getSupabaseAdminClient() {
|
|
||||||
if (cachedClient) return cachedClient
|
|
||||||
|
|
||||||
const supabaseUrl = Bun.env.SUPABASE_URL ?? Bun.env.VITE_SUPABASE_URL
|
|
||||||
const supabaseServiceRoleKey = Bun.env.SUPABASE_SERVICE_ROLE_KEY
|
|
||||||
|
|
||||||
if (!supabaseUrl || !supabaseServiceRoleKey) {
|
|
||||||
throw new Error(
|
|
||||||
'Missing Supabase admin env vars. Set SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY in backend environment.',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedClient = createClient(supabaseUrl, supabaseServiceRoleKey, {
|
|
||||||
auth: {
|
|
||||||
autoRefreshToken: false,
|
|
||||||
persistSession: false,
|
|
||||||
detectSessionInUrl: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return cachedClient
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { createClient } from '@supabase/supabase-js'
|
|
||||||
|
|
||||||
const supabaseUrl = Bun.env.SUPABASE_URL ?? Bun.env.VITE_SUPABASE_URL
|
|
||||||
const supabaseAnonKey =
|
|
||||||
Bun.env.SUPABASE_ANON_KEY ?? Bun.env.VITE_SUPABASE_ANON_KEY
|
|
||||||
|
|
||||||
if (!supabaseUrl || !supabaseAnonKey) {
|
|
||||||
throw new Error(
|
|
||||||
'Missing Supabase env vars. Set SUPABASE_URL/SUPABASE_ANON_KEY (or VITE_SUPABASE_URL/VITE_SUPABASE_ANON_KEY) in backend environment.',
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
|
|
||||||
auth: {
|
|
||||||
autoRefreshToken: false,
|
|
||||||
persistSession: false,
|
|
||||||
detectSessionInUrl: false,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,50 +1,18 @@
|
|||||||
import type { MiddlewareHandler } from 'hono'
|
import { auth } from '@/lib/auth';
|
||||||
import { db } from '@/lib/prisma'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { supabase } from '@/lib/supabase'
|
import { createMiddleware } from 'hono/factory';
|
||||||
import type { AppEnv } from '@/types/hono'
|
|
||||||
|
|
||||||
function getBearerToken(value: string | undefined): string | null {
|
export const requireAuth = createMiddleware<AppEnv>(async (c, next) => {
|
||||||
if (!value) return null
|
const session = await auth.api.getSession({
|
||||||
const [scheme, token] = value.split(' ')
|
headers: c.req.raw.headers,
|
||||||
if (scheme?.toLowerCase() !== 'bearer' || !token) return null
|
});
|
||||||
return token
|
|
||||||
|
if (!session) {
|
||||||
|
return c.json({ message: 'Unauthorized' }, 401);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requireAuth: MiddlewareHandler<AppEnv> = async (
|
c.set('user', session.user);
|
||||||
c,
|
c.set('session', session.session);
|
||||||
next,
|
|
||||||
) => {
|
|
||||||
const token = getBearerToken(c.req.header('authorization'))
|
|
||||||
|
|
||||||
if (!token) {
|
await next();
|
||||||
return c.json({ message: 'Missing bearer token.' }, 403)
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const { data, error } = await supabase.auth.getUser(token)
|
|
||||||
|
|
||||||
if (error || !data.user) {
|
|
||||||
return c.json({ message: 'Invalid or expired token.' }, 403)
|
|
||||||
}
|
|
||||||
|
|
||||||
const email = data.user.email?.toLowerCase()
|
|
||||||
|
|
||||||
if (!email) {
|
|
||||||
return c.json({ message: 'Auth user does not contain email.' }, 403)
|
|
||||||
}
|
|
||||||
|
|
||||||
const appUser = await db.user.findFirst({
|
|
||||||
where: {
|
|
||||||
supabaseUserId: data.user.id,
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
select: { id: true },
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!appUser) {
|
|
||||||
return c.json({ message: 'User not provisioned in backend.' }, 403)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.set('authUser', data.user)
|
|
||||||
c.set('appUserId', appUser.id)
|
|
||||||
await next()
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,35 +1,29 @@
|
|||||||
import type { MiddlewareHandler } from 'hono'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import type { MiddlewareHandler } from 'hono';
|
||||||
|
|
||||||
function getSuperAdminEmails(): Set<string> {
|
function getSuperAdminEmails(): Set<string> {
|
||||||
const raw = Bun.env.SUPER_ADMIN_EMAILS ?? ''
|
const raw = Bun.env.SUPER_ADMIN_EMAILS ?? '';
|
||||||
const emails = raw
|
const emails = raw
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((value) => value.trim().toLowerCase())
|
.map((value) => value.trim().toLowerCase())
|
||||||
.filter(Boolean)
|
.filter(Boolean);
|
||||||
return new Set(emails)
|
return new Set(emails);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const requireSuperAdmin: MiddlewareHandler<AppEnv> = async (
|
export const requireSuperAdmin: MiddlewareHandler<AppEnv> = async (c, next) => {
|
||||||
c,
|
const user = c.get('user');
|
||||||
next,
|
const role = (user as any).role || 'member';
|
||||||
) => {
|
const email = user.email.toLowerCase();
|
||||||
const authUser = c.get('authUser')
|
|
||||||
const role =
|
|
||||||
typeof authUser.app_metadata?.role === 'string'
|
|
||||||
? authUser.app_metadata.role
|
|
||||||
: null
|
|
||||||
const email = authUser.email?.toLowerCase()
|
|
||||||
|
|
||||||
if (role === 'super_admin') {
|
if (role === 'super_admin') {
|
||||||
await next()
|
await next();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (email && getSuperAdminEmails().has(email)) {
|
if (email && getSuperAdminEmails().has(email)) {
|
||||||
await next()
|
await next();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.json({ message: 'Este recurso requiere permisos de super admin.' }, 403)
|
return c.json({ message: 'Este recurso requiere permisos de super admin.' }, 403);
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
|
import { createAdminBookingHandler } from '@/modules/admin-booking/handlers/create-admin-booking.handler';
|
||||||
|
import { listAdminBookingsHandler } from '@/modules/admin-booking/handlers/list-admin-bookings.handler';
|
||||||
|
import { updateAdminBookingStatusHandler } from '@/modules/admin-booking/handlers/update-admin-booking-status.handler';
|
||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import { zValidator } from '@hono/zod-validator';
|
||||||
|
import {
|
||||||
|
createAdminBookingSchema,
|
||||||
|
listAdminBookingsQuerySchema,
|
||||||
|
updateAdminBookingStatusSchema,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { Hono } from 'hono';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const adminBookingRoutes = new Hono<AppEnv>();
|
||||||
|
const complexIdParamsSchema = z.object({ complexId: z.uuid() });
|
||||||
|
const bookingIdParamsSchema = z.object({ id: z.uuid() });
|
||||||
|
|
||||||
|
adminBookingRoutes.use('*', requireAuth);
|
||||||
|
|
||||||
|
adminBookingRoutes.get(
|
||||||
|
'/complex/:complexId',
|
||||||
|
zValidator('param', complexIdParamsSchema),
|
||||||
|
zValidator('query', listAdminBookingsQuerySchema),
|
||||||
|
listAdminBookingsHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
adminBookingRoutes.post(
|
||||||
|
'/complex/:complexId',
|
||||||
|
zValidator('param', complexIdParamsSchema),
|
||||||
|
zValidator('json', createAdminBookingSchema),
|
||||||
|
createAdminBookingHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
adminBookingRoutes.patch(
|
||||||
|
'/:id/status',
|
||||||
|
zValidator('param', bookingIdParamsSchema),
|
||||||
|
zValidator('json', updateAdminBookingStatusSchema),
|
||||||
|
updateAdminBookingStatusHandler
|
||||||
|
);
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import {
|
||||||
|
AdminBookingServiceError,
|
||||||
|
createAdminBooking,
|
||||||
|
} from '@/modules/admin-booking/services/admin-booking.service';
|
||||||
|
import { sendBookingConfirmation } from '@/services/booking-email.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { CreateAdminBookingInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
type ComplexIdParams = { complexId: string };
|
||||||
|
|
||||||
|
export async function createAdminBookingHandler(c: AppContext) {
|
||||||
|
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
|
const payload = c.req.valid('json' as never) as CreateAdminBookingInput;
|
||||||
|
const user = c.get('user');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const booking = await createAdminBooking(user.id, complexId, payload);
|
||||||
|
|
||||||
|
void sendBookingConfirmation({
|
||||||
|
bookingCode: booking.bookingCode,
|
||||||
|
complexName: booking.complexName,
|
||||||
|
date: booking.date,
|
||||||
|
startTime: booking.startTime,
|
||||||
|
endTime: booking.endTime,
|
||||||
|
courtName: booking.courtName,
|
||||||
|
sportName: booking.sport.name,
|
||||||
|
customerName: booking.customerName,
|
||||||
|
customerEmail: booking.customerEmail,
|
||||||
|
price: booking.price,
|
||||||
|
});
|
||||||
|
|
||||||
|
return c.json(booking, 201);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
return c.json({ message: error.message }, error.status);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
AdminBookingServiceError,
|
||||||
|
listAdminBookings,
|
||||||
|
} from '@/modules/admin-booking/services/admin-booking.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { ListAdminBookingsQuery } from '@repo/api-contract';
|
||||||
|
|
||||||
|
type ComplexIdParams = { complexId: string };
|
||||||
|
|
||||||
|
export async function listAdminBookingsHandler(c: AppContext) {
|
||||||
|
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
|
const query = c.req.valid('query' as never) as ListAdminBookingsQuery;
|
||||||
|
const user = c.get('user');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await listAdminBookings(user.id, complexId, query);
|
||||||
|
return c.json(response);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
return c.json({ message: error.message }, error.status);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
AdminBookingServiceError,
|
||||||
|
updateAdminBookingStatus,
|
||||||
|
} from '@/modules/admin-booking/services/admin-booking.service';
|
||||||
|
import { sendBookingCancelled, sendBookingNoShow } from '@/services/booking-email.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { UpdateAdminBookingStatusInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
type BookingIdParams = { id: string };
|
||||||
|
|
||||||
|
export async function updateAdminBookingStatusHandler(c: AppContext) {
|
||||||
|
const { id } = c.req.valid('param' as never) as BookingIdParams;
|
||||||
|
const payload = c.req.valid('json' as never) as UpdateAdminBookingStatusInput;
|
||||||
|
const user = c.get('user');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const booking = await updateAdminBookingStatus(user.id, id, payload);
|
||||||
|
|
||||||
|
if (payload.status === 'CANCELLED') {
|
||||||
|
void sendBookingCancelled({
|
||||||
|
bookingCode: booking.bookingCode,
|
||||||
|
complexName: booking.complexName,
|
||||||
|
date: booking.date,
|
||||||
|
startTime: booking.startTime,
|
||||||
|
endTime: booking.endTime,
|
||||||
|
courtName: booking.courtName,
|
||||||
|
sportName: booking.sport.name,
|
||||||
|
customerName: booking.customerName,
|
||||||
|
customerEmail: booking.customerEmail,
|
||||||
|
});
|
||||||
|
} else if (payload.status === 'NOSHOW') {
|
||||||
|
void sendBookingNoShow({
|
||||||
|
bookingCode: booking.bookingCode,
|
||||||
|
complexName: booking.complexName,
|
||||||
|
date: booking.date,
|
||||||
|
startTime: booking.startTime,
|
||||||
|
endTime: booking.endTime,
|
||||||
|
courtName: booking.courtName,
|
||||||
|
sportName: booking.sport.name,
|
||||||
|
customerName: booking.customerName,
|
||||||
|
customerEmail: booking.customerEmail,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json(booking);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
return c.json({ message: error.message }, error.status);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,592 @@
|
|||||||
|
import { randomInt } from 'node:crypto';
|
||||||
|
import { CourtBookingStatus } from '@/generated/prisma/enums';
|
||||||
|
import { db } from '@/lib/prisma';
|
||||||
|
import { evaluatePlanUsage, parsePlanRules } from '@/modules/plan/services/plan-rules.service';
|
||||||
|
import type { DayOfWeek } from '@repo/api-contract';
|
||||||
|
import type {
|
||||||
|
AdminBooking,
|
||||||
|
CreateAdminBookingInput,
|
||||||
|
ListAdminBookingsQuery,
|
||||||
|
UpdateAdminBookingStatusInput,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
|
type Slot = {
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DAY_OF_WEEK_BY_INDEX = [
|
||||||
|
'SUNDAY',
|
||||||
|
'MONDAY',
|
||||||
|
'TUESDAY',
|
||||||
|
'WEDNESDAY',
|
||||||
|
'THURSDAY',
|
||||||
|
'FRIDAY',
|
||||||
|
'SATURDAY',
|
||||||
|
] as const;
|
||||||
|
const BOOKING_CODE_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
|
||||||
|
const BOOKING_CODE_LENGTH = 6;
|
||||||
|
|
||||||
|
export class AdminBookingServiceError extends Error {
|
||||||
|
status: 400 | 403 | 404 | 409;
|
||||||
|
|
||||||
|
constructor(message: string, status: 400 | 403 | 404 | 409) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'AdminBookingServiceError';
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toMinutes(value: string): number {
|
||||||
|
const [hours, minutes] = value.split(':').map((part) => Number(part));
|
||||||
|
return hours * 60 + minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function minutesToTime(minutes: number): string {
|
||||||
|
const safeMinutes = Math.max(0, minutes);
|
||||||
|
const hours = Math.floor(safeMinutes / 60);
|
||||||
|
const mins = safeMinutes % 60;
|
||||||
|
return `${String(hours).padStart(2, '0')}:${String(mins).padStart(2, '0')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIsoDate(date: string): Date {
|
||||||
|
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(date);
|
||||||
|
|
||||||
|
if (!match) {
|
||||||
|
throw new AdminBookingServiceError('La fecha debe tener formato YYYY-MM-DD.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const year = Number(match[1]);
|
||||||
|
const month = Number(match[2]);
|
||||||
|
const day = Number(match[3]);
|
||||||
|
|
||||||
|
const bookingDate = new Date(Date.UTC(year, month - 1, day));
|
||||||
|
|
||||||
|
if (
|
||||||
|
Number.isNaN(bookingDate.getTime()) ||
|
||||||
|
bookingDate.getUTCFullYear() !== year ||
|
||||||
|
bookingDate.getUTCMonth() + 1 !== month ||
|
||||||
|
bookingDate.getUTCDate() !== day
|
||||||
|
) {
|
||||||
|
throw new AdminBookingServiceError('La fecha enviada no es valida.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bookingDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDayOfWeek(date: Date) {
|
||||||
|
const dayOfWeek = DAY_OF_WEEK_BY_INDEX[date.getUTCDay()];
|
||||||
|
|
||||||
|
if (!dayOfWeek) {
|
||||||
|
throw new AdminBookingServiceError('No se pudo resolver el dia de la semana.', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dayOfWeek;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatIsoDate(date: Date): string {
|
||||||
|
return date.toISOString().slice(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateBookingCode(): string {
|
||||||
|
let code = '';
|
||||||
|
|
||||||
|
for (let index = 0; index < BOOKING_CODE_LENGTH; index += 1) {
|
||||||
|
code += BOOKING_CODE_ALPHABET[randomInt(0, BOOKING_CODE_ALPHABET.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSlots(
|
||||||
|
availability: Array<{
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
}>,
|
||||||
|
slotDurationMinutes: number
|
||||||
|
): Slot[] {
|
||||||
|
const slots: Slot[] = [];
|
||||||
|
|
||||||
|
for (const range of availability) {
|
||||||
|
const start = toMinutes(range.startTime);
|
||||||
|
const end = toMinutes(range.endTime);
|
||||||
|
|
||||||
|
for (
|
||||||
|
let current = start;
|
||||||
|
current + slotDurationMinutes <= end;
|
||||||
|
current += slotDurationMinutes
|
||||||
|
) {
|
||||||
|
slots.push({
|
||||||
|
startTime: minutesToTime(current),
|
||||||
|
endTime: minutesToTime(current + slotDurationMinutes),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureComplexAccess(complexId: string, userId: string) {
|
||||||
|
const complexUser = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
plan: {
|
||||||
|
select: {
|
||||||
|
rules: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!complexUser) {
|
||||||
|
throw new AdminBookingServiceError('No tienes permisos para administrar este complejo.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return complexUser.complex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolvePrice(
|
||||||
|
court: {
|
||||||
|
basePrice: unknown;
|
||||||
|
priceRules: Array<{
|
||||||
|
dayOfWeek: string | null;
|
||||||
|
startTime: string | null;
|
||||||
|
endTime: string | null;
|
||||||
|
price: unknown;
|
||||||
|
}>;
|
||||||
|
},
|
||||||
|
dayOfWeek: string,
|
||||||
|
startTime: string,
|
||||||
|
endTime: string
|
||||||
|
): number {
|
||||||
|
const slotStart = toMinutes(startTime);
|
||||||
|
const slotEnd = toMinutes(endTime);
|
||||||
|
|
||||||
|
const matchingRules = court.priceRules
|
||||||
|
.filter((rule) => {
|
||||||
|
if (rule.dayOfWeek && rule.dayOfWeek !== dayOfWeek) return false;
|
||||||
|
if (!rule.startTime || !rule.endTime) return true;
|
||||||
|
return slotStart >= toMinutes(rule.startTime) && slotEnd <= toMinutes(rule.endTime);
|
||||||
|
})
|
||||||
|
.sort((first, second) => {
|
||||||
|
const firstSpecificity =
|
||||||
|
(first.dayOfWeek ? 2 : 0) + (first.startTime && first.endTime ? 1 : 0);
|
||||||
|
const secondSpecificity =
|
||||||
|
(second.dayOfWeek ? 2 : 0) + (second.startTime && second.endTime ? 1 : 0);
|
||||||
|
return secondSpecificity - firstSpecificity;
|
||||||
|
});
|
||||||
|
|
||||||
|
return Number(matchingRules[0]?.price ?? court.basePrice);
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapBookingResponse(booking: {
|
||||||
|
id: string;
|
||||||
|
bookingCode: string;
|
||||||
|
bookingDate: Date;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
customerName: string;
|
||||||
|
customerPhone: string;
|
||||||
|
customerEmail: string | null;
|
||||||
|
status: 'CONFIRMED' | 'CANCELLED' | 'COMPLETED' | 'NOSHOW';
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
|
court: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
complex: {
|
||||||
|
id: string;
|
||||||
|
complexName: string;
|
||||||
|
};
|
||||||
|
sport: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
price?: number;
|
||||||
|
}): AdminBooking {
|
||||||
|
return {
|
||||||
|
id: booking.id,
|
||||||
|
bookingCode: booking.bookingCode,
|
||||||
|
complexId: booking.court.complex.id,
|
||||||
|
complexName: booking.court.complex.complexName,
|
||||||
|
courtId: booking.court.id,
|
||||||
|
courtName: booking.court.name,
|
||||||
|
sport: {
|
||||||
|
id: booking.court.sport.id,
|
||||||
|
name: booking.court.sport.name,
|
||||||
|
slug: booking.court.sport.slug,
|
||||||
|
},
|
||||||
|
date: formatIsoDate(booking.bookingDate),
|
||||||
|
startTime: booking.startTime,
|
||||||
|
endTime: booking.endTime,
|
||||||
|
customerName: booking.customerName,
|
||||||
|
customerPhone: booking.customerPhone,
|
||||||
|
customerEmail: booking.customerEmail ?? undefined,
|
||||||
|
price: booking.price ?? 0,
|
||||||
|
status: booking.status,
|
||||||
|
createdAt: booking.createdAt.toISOString(),
|
||||||
|
updatedAt: booking.updatedAt.toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listAdminBookings(
|
||||||
|
userId: string,
|
||||||
|
complexId: string,
|
||||||
|
query: ListAdminBookingsQuery
|
||||||
|
) {
|
||||||
|
await ensureComplexAccess(complexId, userId);
|
||||||
|
const fromDate = parseIsoDate(query.fromDate);
|
||||||
|
|
||||||
|
const bookings = await db.courtBooking.findMany({
|
||||||
|
where: {
|
||||||
|
bookingDate: {
|
||||||
|
gte: fromDate,
|
||||||
|
},
|
||||||
|
court: {
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
court: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: [{ bookingDate: 'asc' }, { startTime: 'asc' }, { createdAt: 'asc' }],
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = bookings.map((booking) => mapBookingResponse(booking));
|
||||||
|
|
||||||
|
return {
|
||||||
|
bookings: response,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createAdminBooking(
|
||||||
|
userId: string,
|
||||||
|
complexId: string,
|
||||||
|
input: CreateAdminBookingInput
|
||||||
|
) {
|
||||||
|
const complex = await ensureComplexAccess(complexId, userId);
|
||||||
|
|
||||||
|
const adminUser = await db.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
select: { emailVerified: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!adminUser?.emailVerified) {
|
||||||
|
throw new AdminBookingServiceError('Debés verificar tu email para poder crear reservas.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bookingDate = parseIsoDate(input.date);
|
||||||
|
const dayOfWeek = getDayOfWeek(bookingDate);
|
||||||
|
|
||||||
|
const court = await db.court.findFirst({
|
||||||
|
where: {
|
||||||
|
id: input.courtId,
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
availabilities: {
|
||||||
|
where: {
|
||||||
|
dayOfWeek,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
startTime: 'asc',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
priceRules: {
|
||||||
|
where: { isActive: true },
|
||||||
|
orderBy: [{ dayOfWeek: 'asc' }, { startTime: 'asc' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!court) {
|
||||||
|
throw new AdminBookingServiceError('La cancha seleccionada no existe en el complejo.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validSlots = buildSlots(court.availabilities, court.slotDurationMinutes);
|
||||||
|
const selectedStartMinutes = toMinutes(input.startTime);
|
||||||
|
const selectedEndMinutes = selectedStartMinutes + court.slotDurationMinutes;
|
||||||
|
const selectedSlot = {
|
||||||
|
startTime: input.startTime,
|
||||||
|
endTime: minutesToTime(selectedEndMinutes),
|
||||||
|
};
|
||||||
|
|
||||||
|
const slotExists = validSlots.some(
|
||||||
|
(slot) => slot.startTime === selectedSlot.startTime && slot.endTime === selectedSlot.endTime
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!slotExists) {
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'El horario seleccionado no esta disponible para esa cancha.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let attempt = 0; attempt < 5; attempt += 1) {
|
||||||
|
try {
|
||||||
|
const booking = await db.$transaction(async (tx) => {
|
||||||
|
if (complex.plan) {
|
||||||
|
const rules = parsePlanRules(complex.plan.rules);
|
||||||
|
const bookingsForDate = await tx.courtBooking.count({
|
||||||
|
where: {
|
||||||
|
bookingDate,
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
court: {
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const courtsCount = await tx.court.count({
|
||||||
|
where: {
|
||||||
|
complexId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const violations = evaluatePlanUsage(rules, {
|
||||||
|
courtsCount,
|
||||||
|
bookingsToday: bookingsForDate,
|
||||||
|
});
|
||||||
|
|
||||||
|
const maxBookingsViolation = violations.find(
|
||||||
|
(violation) => violation.code === 'MAX_BOOKINGS_PER_DAY_REACHED'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (maxBookingsViolation) {
|
||||||
|
throw new AdminBookingServiceError(maxBookingsViolation.message, 409);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const overlappingBooking = await tx.courtBooking.findFirst({
|
||||||
|
where: {
|
||||||
|
courtId: court.id,
|
||||||
|
bookingDate,
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
startTime: {
|
||||||
|
lt: selectedSlot.endTime,
|
||||||
|
},
|
||||||
|
endTime: {
|
||||||
|
gt: selectedSlot.startTime,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (overlappingBooking) {
|
||||||
|
throw new AdminBookingServiceError('El horario seleccionado ya fue reservado.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.courtBooking.create({
|
||||||
|
data: {
|
||||||
|
id: uuidv7(),
|
||||||
|
bookingCode: generateBookingCode(),
|
||||||
|
courtId: court.id,
|
||||||
|
bookingDate,
|
||||||
|
startTime: selectedSlot.startTime,
|
||||||
|
endTime: selectedSlot.endTime,
|
||||||
|
customerName: input.customerName.trim(),
|
||||||
|
customerPhone: input.customerPhone.trim(),
|
||||||
|
customerEmail: input.customerEmail?.trim() || null,
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
court: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const price = resolvePrice(court, dayOfWeek, selectedSlot.startTime, selectedSlot.endTime);
|
||||||
|
|
||||||
|
return mapBookingResponse({ ...booking, price });
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof AdminBookingServiceError) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
const prismaError = error as {
|
||||||
|
code?: string;
|
||||||
|
meta?: {
|
||||||
|
target?: string[] | string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (prismaError.code === 'P2002') {
|
||||||
|
const targets = Array.isArray(prismaError.meta?.target)
|
||||||
|
? prismaError.meta?.target
|
||||||
|
: [prismaError.meta?.target];
|
||||||
|
const isBookingCodeCollision = targets.some((target) =>
|
||||||
|
String(target).includes('booking_code')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isBookingCodeCollision) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AdminBookingServiceError('El horario seleccionado ya fue reservado.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'No se pudo generar un codigo de reserva unico. Intenta nuevamente.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateAdminBookingStatus(
|
||||||
|
userId: string,
|
||||||
|
bookingId: string,
|
||||||
|
input: UpdateAdminBookingStatusInput
|
||||||
|
) {
|
||||||
|
const booking = await db.courtBooking.findFirst({
|
||||||
|
where: {
|
||||||
|
id: bookingId,
|
||||||
|
court: {
|
||||||
|
complex: {
|
||||||
|
users: {
|
||||||
|
some: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
court: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
sport: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
slug: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!booking) {
|
||||||
|
throw new AdminBookingServiceError('Reserva no encontrada.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.status === 'CANCELLED' && booking.status !== 'CONFIRMED') {
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'Solo se pueden cancelar reservas en estado confirmada.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.status === 'COMPLETED' && booking.status !== 'CONFIRMED') {
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'Solo se pueden marcar como cumplidas las reservas confirmadas.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.status === 'NOSHOW' && booking.status !== 'CONFIRMED') {
|
||||||
|
throw new AdminBookingServiceError(
|
||||||
|
'Solo se pueden marcar como no show las reservas confirmadas.',
|
||||||
|
409
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.courtBookingLog.create({
|
||||||
|
data: {
|
||||||
|
id: uuidv7(),
|
||||||
|
bookingCode: booking.bookingCode,
|
||||||
|
courtId: booking.court.id,
|
||||||
|
bookingDate: booking.bookingDate,
|
||||||
|
startTime: booking.startTime,
|
||||||
|
endTime: booking.endTime,
|
||||||
|
customerName: booking.customerName,
|
||||||
|
customerPhone: booking.customerPhone,
|
||||||
|
customerEmail: booking.customerEmail,
|
||||||
|
previousStatus: booking.status,
|
||||||
|
newStatus: input.status,
|
||||||
|
changedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (input.status === 'COMPLETED' || input.status === 'NOSHOW') {
|
||||||
|
await db.courtBooking.update({
|
||||||
|
where: { id: booking.id },
|
||||||
|
data: {
|
||||||
|
status: input.status,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// if the booking is cancelled we delete it to free up the slot, but we keep a log of it with the cancelled status
|
||||||
|
await db.courtBooking.delete({
|
||||||
|
where: { id: booking.id },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapBookingResponse({ ...booking, status: input.status });
|
||||||
|
}
|
||||||
9
apps/backend/src/modules/auth/auth.routes.ts
Normal file
9
apps/backend/src/modules/auth/auth.routes.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { auth } from '@/lib/auth';
|
||||||
|
import { AppEnv } from '@/types/hono';
|
||||||
|
import { Hono } from 'hono';
|
||||||
|
|
||||||
|
export const authRoutes = new Hono<AppEnv>();
|
||||||
|
|
||||||
|
authRoutes.on(['POST', 'GET'], '/api/auth/*', (c) => {
|
||||||
|
return auth.handler(c.req.raw);
|
||||||
|
});
|
||||||
@@ -1,37 +1,80 @@
|
|||||||
import { zValidator } from '@hono/zod-validator'
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
import { createComplexSchema, updateComplexSchema } from '@repo/api-contract'
|
import { acceptComplexInvitationsHandler } from '@/modules/complex/handlers/accept-complex-invitations.handler';
|
||||||
import { Hono } from 'hono'
|
import { cancelComplexInvitationHandler } from '@/modules/complex/handlers/cancel-complex-invitation.handler';
|
||||||
import { z } from 'zod'
|
import { createComplexHandler } from '@/modules/complex/handlers/create-complex.handler';
|
||||||
import { requireAuth } from '@/middlewares/require-auth.middleware'
|
import { getComplexByIdHandler } from '@/modules/complex/handlers/get-complex-by-id.handler';
|
||||||
import { createComplexHandler } from '@/modules/complex/handlers/create-complex.handler'
|
import { getComplexBySlugHandler } from '@/modules/complex/handlers/get-complex-by-slug.handler';
|
||||||
import { getComplexByIdHandler } from '@/modules/complex/handlers/get-complex-by-id.handler'
|
import { getCurrentComplexHandler } from '@/modules/complex/handlers/get-current-complex.handler';
|
||||||
import { getComplexBySlugHandler } from '@/modules/complex/handlers/get-complex-by-slug.handler'
|
import { inviteComplexUserHandler } from '@/modules/complex/handlers/invite-complex-user.handler';
|
||||||
import { listMyComplexesHandler } from '@/modules/complex/handlers/list-my-complexes.handler'
|
import { listComplexUsersHandler } from '@/modules/complex/handlers/list-complex-users.handler';
|
||||||
import { updateComplexHandler } from '@/modules/complex/handlers/update-complex.handler'
|
import { listMyComplexesHandler } from '@/modules/complex/handlers/list-my-complexes.handler';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { resendComplexInvitationHandler } from '@/modules/complex/handlers/resend-complex-invitation.handler';
|
||||||
|
import { revokeComplexUserHandler } from '@/modules/complex/handlers/revoke-complex-user.handler';
|
||||||
|
import { selectComplexHandler } from '@/modules/complex/handlers/select-complex.handler';
|
||||||
|
import { updateComplexHandler } from '@/modules/complex/handlers/update-complex.handler';
|
||||||
|
import type { AppEnv } from '@/types/hono';
|
||||||
|
import { zValidator } from '@hono/zod-validator';
|
||||||
|
import {
|
||||||
|
acceptComplexInvitationsSchema,
|
||||||
|
createComplexSchema,
|
||||||
|
inviteComplexUserSchema,
|
||||||
|
updateComplexSchema,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { Hono } from 'hono';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const complexRoutes = new Hono<AppEnv>()
|
export const complexRoutes = new Hono<AppEnv>();
|
||||||
const complexIdParamsSchema = z.object({ id: z.uuid() })
|
const complexIdParamsSchema = z.object({ id: z.uuid() });
|
||||||
const complexSlugParamsSchema = z.object({ slug: z.string().trim().min(1) })
|
const complexSlugParamsSchema = z.object({ slug: z.string().trim().min(1) });
|
||||||
|
|
||||||
complexRoutes.use('*', requireAuth)
|
complexRoutes.use('*', requireAuth);
|
||||||
|
|
||||||
complexRoutes.post('/', zValidator('json', createComplexSchema), createComplexHandler)
|
complexRoutes.post('/', zValidator('json', createComplexSchema), createComplexHandler);
|
||||||
complexRoutes.get('/mine', listMyComplexesHandler)
|
complexRoutes.get('/me', getCurrentComplexHandler);
|
||||||
|
complexRoutes.post('/select', selectComplexHandler);
|
||||||
|
complexRoutes.post(
|
||||||
|
'/accept-pending',
|
||||||
|
zValidator('json', acceptComplexInvitationsSchema),
|
||||||
|
acceptComplexInvitationsHandler
|
||||||
|
);
|
||||||
|
complexRoutes.get('/mine', listMyComplexesHandler);
|
||||||
complexRoutes.get(
|
complexRoutes.get(
|
||||||
'/slug/:slug',
|
'/slug/:slug',
|
||||||
zValidator('param', complexSlugParamsSchema),
|
zValidator('param', complexSlugParamsSchema),
|
||||||
getComplexBySlugHandler,
|
getComplexBySlugHandler
|
||||||
)
|
);
|
||||||
|
|
||||||
complexRoutes.get(
|
complexRoutes.get(
|
||||||
'/:id',
|
'/:id/users',
|
||||||
zValidator('param', complexIdParamsSchema),
|
zValidator('param', complexIdParamsSchema),
|
||||||
getComplexByIdHandler,
|
listComplexUsersHandler
|
||||||
)
|
);
|
||||||
|
complexRoutes.post(
|
||||||
|
'/:id/users',
|
||||||
|
zValidator('param', complexIdParamsSchema),
|
||||||
|
zValidator('json', inviteComplexUserSchema),
|
||||||
|
inviteComplexUserHandler
|
||||||
|
);
|
||||||
|
complexRoutes.post(
|
||||||
|
'/:id/invitations/:invitationId/resend',
|
||||||
|
zValidator('param', z.object({ id: z.uuid(), invitationId: z.uuid() })),
|
||||||
|
resendComplexInvitationHandler
|
||||||
|
);
|
||||||
|
complexRoutes.delete(
|
||||||
|
'/:id/invitations/:invitationId',
|
||||||
|
zValidator('param', z.object({ id: z.uuid(), invitationId: z.uuid() })),
|
||||||
|
cancelComplexInvitationHandler
|
||||||
|
);
|
||||||
|
complexRoutes.delete(
|
||||||
|
'/:id/users/:userId',
|
||||||
|
zValidator('param', z.object({ id: z.uuid(), userId: z.string().min(1) })),
|
||||||
|
revokeComplexUserHandler
|
||||||
|
);
|
||||||
|
|
||||||
|
complexRoutes.get('/:id', zValidator('param', complexIdParamsSchema), getComplexByIdHandler);
|
||||||
complexRoutes.patch(
|
complexRoutes.patch(
|
||||||
'/:id',
|
'/:id',
|
||||||
zValidator('param', complexIdParamsSchema),
|
zValidator('param', complexIdParamsSchema),
|
||||||
zValidator('json', updateComplexSchema),
|
zValidator('json', updateComplexSchema),
|
||||||
updateComplexHandler,
|
updateComplexHandler
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
ComplexMembersError,
|
||||||
|
acceptComplexInvitations,
|
||||||
|
} from '@/modules/complex/services/complex-members.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { AcceptComplexInvitationsInput } from '@repo/api-contract';
|
||||||
|
|
||||||
|
export async function acceptComplexInvitationsHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const payload = c.req.valid('json' as never) as AcceptComplexInvitationsInput;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await acceptComplexInvitations(user.id, user.email, payload);
|
||||||
|
return c.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ComplexMembersError) {
|
||||||
|
return c.json(
|
||||||
|
{ message: error.message },
|
||||||
|
{
|
||||||
|
status: error.status,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
ComplexMembersError,
|
||||||
|
cancelComplexInvitation,
|
||||||
|
} from '@/modules/complex/services/complex-members.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const cancelInvitationParamsSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
invitationId: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function cancelComplexInvitationHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const params = cancelInvitationParamsSchema.parse(c.req.param());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await cancelComplexInvitation(user.id, params.id, params.invitationId);
|
||||||
|
return c.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ComplexMembersError) {
|
||||||
|
return c.json({ message: error.message }, { status: error.status });
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +1,51 @@
|
|||||||
import type { CreateComplexInput } from '@repo/api-contract'
|
import { db } from '@/lib/prisma';
|
||||||
import type { AppContext } from '@/types/hono'
|
import { createComplex } from '@/modules/complex/services/complex.service';
|
||||||
import { createComplex } from '@/modules/complex/services/complex.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { CreateComplexInput } from '@repo/api-contract';
|
||||||
|
|
||||||
export async function createComplexHandler(c: AppContext) {
|
export async function createComplexHandler(c: AppContext) {
|
||||||
const payload = c.req.valid('json' as never) as CreateComplexInput
|
const payload = c.req.valid('json' as never) as CreateComplexInput;
|
||||||
|
|
||||||
const authUser = c.get('authUser')
|
const user = c.get('user');
|
||||||
const adminEmail = authUser.email
|
let adminEmail = user?.email;
|
||||||
|
let userId = user?.id;
|
||||||
|
|
||||||
if (!adminEmail) {
|
if (!adminEmail) {
|
||||||
return c.json({ message: 'El usuario autenticado no tiene email.' }, 400)
|
const body = await c.req.json().catch(() => ({}));
|
||||||
|
adminEmail = body.adminEmail;
|
||||||
|
userId = body.userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!adminEmail) {
|
||||||
|
return c.json({ message: 'Se requiere email del administrador.' }, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userId && adminEmail) {
|
||||||
|
const existingUser = await db.user.findUnique({
|
||||||
|
where: { email: adminEmail },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
if (!existingUser) {
|
||||||
|
return c.json({ message: 'El usuario no existe.' }, 404);
|
||||||
|
}
|
||||||
|
userId = existingUser.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const complex = await createComplex({
|
const complex = await createComplex({
|
||||||
complexName: payload.complexName,
|
complexName: payload.complexName,
|
||||||
physicalAddress: payload.physicalAddress,
|
physicalAddress: payload.physicalAddress,
|
||||||
adminEmail,
|
adminEmail,
|
||||||
|
userId,
|
||||||
planCode: payload.planCode,
|
planCode: payload.planCode,
|
||||||
})
|
city: payload.city,
|
||||||
|
state: payload.state,
|
||||||
|
country: payload.country,
|
||||||
|
setupCourts: payload.setupCourts,
|
||||||
|
courtSportId: payload.courtSportId,
|
||||||
|
courtStartTime: payload.courtStartTime,
|
||||||
|
courtEndTime: payload.courtEndTime,
|
||||||
|
courtDaysOfWeek: payload.courtDaysOfWeek,
|
||||||
|
});
|
||||||
|
|
||||||
return c.json(complex, 201)
|
return c.json(complex, 201);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { getComplexById } from '@/modules/complex/services/complex.service';
|
||||||
import { getComplexById } from '@/modules/complex/services/complex.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
type ComplexIdParams = { id: string }
|
type ComplexIdParams = { id: string };
|
||||||
|
|
||||||
export async function getComplexByIdHandler(c: AppContext) {
|
export async function getComplexByIdHandler(c: AppContext) {
|
||||||
const { id } = c.req.valid('param' as never) as ComplexIdParams
|
const { id } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
|
|
||||||
const complex = await getComplexById(id)
|
const complex = await getComplexById(id);
|
||||||
|
|
||||||
if (!complex) {
|
if (!complex) {
|
||||||
return c.json({ message: 'Complejo no encontrado.' }, 404)
|
return c.json({ message: 'Complejo no encontrado.' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.json(complex)
|
return c.json(complex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { getComplexBySlug } from '@/modules/complex/services/complex.service';
|
||||||
import { getComplexBySlug } from '@/modules/complex/services/complex.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
type ComplexSlugParams = { slug: string }
|
type ComplexSlugParams = { slug: string };
|
||||||
|
|
||||||
export async function getComplexBySlugHandler(c: AppContext) {
|
export async function getComplexBySlugHandler(c: AppContext) {
|
||||||
const { slug } = c.req.valid('param' as never) as ComplexSlugParams
|
const { slug } = c.req.valid('param' as never) as ComplexSlugParams;
|
||||||
|
|
||||||
const complex = await getComplexBySlug(slug)
|
const complex = await getComplexBySlug(slug);
|
||||||
|
|
||||||
if (!complex) {
|
if (!complex) {
|
||||||
return c.json({ message: 'Complejo no encontrado.' }, 404)
|
return c.json({ message: 'Complejo no encontrado.' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.json(complex)
|
return c.json(complex);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { getCurrentComplex } from '@/modules/complex/services/complex.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import { getCookie } from 'hono/cookie';
|
||||||
|
|
||||||
|
const SELECTED_COMPLEX_COOKIE = 'selected-complex-id';
|
||||||
|
|
||||||
|
export async function getCurrentComplexHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const complexId = getCookie(c, SELECTED_COMPLEX_COOKIE);
|
||||||
|
|
||||||
|
if (!complexId) {
|
||||||
|
return c.json(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
const complex = await getCurrentComplex(user.id, complexId);
|
||||||
|
|
||||||
|
if (!complex) {
|
||||||
|
return c.json(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.json(complex);
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
ComplexMembersError,
|
||||||
|
inviteComplexUser,
|
||||||
|
} from '@/modules/complex/services/complex-members.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import type { InviteComplexUserInput } from '@repo/api-contract';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const complexParamsSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
type InviteComplexUserHandlerDeps = {
|
||||||
|
inviteComplexUser: typeof inviteComplexUser;
|
||||||
|
ComplexMembersError: typeof ComplexMembersError;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function createInviteComplexUserHandler(
|
||||||
|
deps: InviteComplexUserHandlerDeps = {
|
||||||
|
inviteComplexUser,
|
||||||
|
ComplexMembersError,
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return async function inviteComplexUserHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const params = complexParamsSchema.parse(c.req.param());
|
||||||
|
const payload = c.req.valid('json' as never) as InviteComplexUserInput;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await deps.inviteComplexUser(user.id, params.id, payload);
|
||||||
|
return c.json(result, 201);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof deps.ComplexMembersError) {
|
||||||
|
return c.json(
|
||||||
|
{ message: error.message },
|
||||||
|
{
|
||||||
|
status: error.status,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const inviteComplexUserHandler = createInviteComplexUserHandler();
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { getComplexUsersOverview } from '@/modules/complex/services/complex-members.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const complexParamsSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function listComplexUsersHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const params = complexParamsSchema.parse(c.req.param());
|
||||||
|
const result = await getComplexUsersOverview(user.id, params.id);
|
||||||
|
return c.json(result);
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import type { AppContext } from '@/types/hono'
|
import { listMyComplexes } from '@/modules/complex/services/complex.service';
|
||||||
import { listMyComplexes } from '@/modules/complex/services/complex.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
|
|
||||||
export async function listMyComplexesHandler(c: AppContext) {
|
export async function listMyComplexesHandler(c: AppContext) {
|
||||||
const appUserId = c.get('appUserId')
|
const user = c.get('user');
|
||||||
const complexes = await listMyComplexes(appUserId)
|
const complexes = await listMyComplexes(user.id);
|
||||||
return c.json(complexes)
|
return c.json(complexes);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import {
|
||||||
|
ComplexMembersError,
|
||||||
|
resendComplexInvitation,
|
||||||
|
} from '@/modules/complex/services/complex-members.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const resendInvitationParamsSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
invitationId: z.uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function resendComplexInvitationHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const params = resendInvitationParamsSchema.parse(c.req.param());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await resendComplexInvitation(user.id, params.id, params.invitationId);
|
||||||
|
return c.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ComplexMembersError) {
|
||||||
|
return c.json({ message: error.message }, { status: error.status });
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import {
|
||||||
|
ComplexMembersError,
|
||||||
|
revokeComplexUser,
|
||||||
|
} from '@/modules/complex/services/complex-members.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const revokeParamsSchema = z.object({
|
||||||
|
id: z.uuid(),
|
||||||
|
userId: z.string().min(1),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function revokeComplexUserHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const params = revokeParamsSchema.parse(c.req.param());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await revokeComplexUser(user.id, params.id, { userId: params.userId });
|
||||||
|
return c.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof ComplexMembersError) {
|
||||||
|
return c.json(
|
||||||
|
{ message: error.message },
|
||||||
|
{
|
||||||
|
status: error.status,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { selectComplex } from '@/modules/complex/services/complex.service';
|
||||||
|
import type { AppContext } from '@/types/hono';
|
||||||
|
import { setCookie } from 'hono/cookie';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
const SELECTED_COMPLEX_COOKIE = 'selected-complex-id';
|
||||||
|
const COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
|
||||||
|
|
||||||
|
const selectComplexSchema = z.object({
|
||||||
|
complexId: z.string().uuid(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function selectComplexHandler(c: AppContext) {
|
||||||
|
const user = c.get('user');
|
||||||
|
const body = await c.req.json();
|
||||||
|
const result = selectComplexSchema.safeParse(body);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
return c.json({ message: 'Invalid payload', issues: result.error.issues }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { complexId } = result.data;
|
||||||
|
const complex = await selectComplex(user.id, complexId);
|
||||||
|
|
||||||
|
if (!complex) {
|
||||||
|
return c.json({ message: 'Complex not found or not associated with user' }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
setCookie(c, SELECTED_COMPLEX_COOKIE, complexId, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: process.env.NODE_ENV === 'production',
|
||||||
|
sameSite: 'strict',
|
||||||
|
path: '/',
|
||||||
|
maxAge: COOKIE_MAX_AGE,
|
||||||
|
});
|
||||||
|
|
||||||
|
return c.json(complex);
|
||||||
|
}
|
||||||
@@ -1,27 +1,27 @@
|
|||||||
import type { UpdateComplexInput } from '@repo/api-contract'
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import { Prisma } from '@/generated/prisma/client'
|
import { getComplexById, updateComplex } from '@/modules/complex/services/complex.service';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { AppContext } from '@/types/hono';
|
||||||
import { getComplexById, updateComplex } from '@/modules/complex/services/complex.service'
|
import type { UpdateComplexInput } from '@repo/api-contract';
|
||||||
|
|
||||||
type ComplexIdParams = { id: string }
|
type ComplexIdParams = { id: string };
|
||||||
|
|
||||||
export async function updateComplexHandler(c: AppContext) {
|
export async function updateComplexHandler(c: AppContext) {
|
||||||
const { id } = c.req.valid('param' as never) as ComplexIdParams
|
const { id } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
const payload = c.req.valid('json' as never) as UpdateComplexInput
|
const payload = c.req.valid('json' as never) as UpdateComplexInput;
|
||||||
|
|
||||||
const existing = await getComplexById(id)
|
const existing = await getComplexById(id);
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
return c.json({ message: 'Complejo no encontrado.' }, 404)
|
return c.json({ message: 'Complejo no encontrado.' }, 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const complex = await updateComplex(id, payload)
|
const complex = await updateComplex(id, payload);
|
||||||
return c.json(complex)
|
return c.json(complex);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
||||||
return c.json({ message: 'No se pudo actualizar el complejo.' }, 409)
|
return c.json({ message: 'No se pudo actualizar el complejo.' }, 409);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,525 @@
|
|||||||
|
import { createHash, randomBytes } from 'node:crypto';
|
||||||
|
import { resolveAvatarUrl } from '@/lib/avatar';
|
||||||
|
import { sendMail } from '@/lib/mailer';
|
||||||
|
import { db } from '@/lib/prisma';
|
||||||
|
import type {
|
||||||
|
AcceptComplexInvitationsInput,
|
||||||
|
AcceptComplexInvitationsResponse,
|
||||||
|
CancelComplexInvitationResponse,
|
||||||
|
ComplexInvitation,
|
||||||
|
ComplexInvitationStatus,
|
||||||
|
ComplexUsersOverview,
|
||||||
|
InviteComplexUserInput,
|
||||||
|
InviteComplexUserResponse,
|
||||||
|
RevokeComplexUserInput,
|
||||||
|
} from '@repo/api-contract';
|
||||||
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
|
const INVITATION_TTL_DAYS = Number(Bun.env.COMPLEX_INVITATION_TTL_DAYS ?? 7);
|
||||||
|
const APP_BASE_URL = Bun.env.APP_BASE_URL ?? 'http://localhost:5173';
|
||||||
|
|
||||||
|
export class ComplexMembersError extends Error {
|
||||||
|
status: 400 | 403 | 404 | 409;
|
||||||
|
|
||||||
|
constructor(message: string, status: 400 | 403 | 404 | 409 = 400) {
|
||||||
|
super(message);
|
||||||
|
this.name = 'ComplexMembersError';
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeEmail(email: string): string {
|
||||||
|
return email.trim().toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function hashToken(token: string): string {
|
||||||
|
return createHash('sha256').update(token).digest('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createInvitationToken(): string {
|
||||||
|
return randomBytes(32).toString('hex');
|
||||||
|
}
|
||||||
|
|
||||||
|
function nowPlusDays(days: number): Date {
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() + days);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInvitationStatus(invitation: {
|
||||||
|
expiresAt: Date;
|
||||||
|
acceptedAt: Date | null;
|
||||||
|
revokedAt: Date | null;
|
||||||
|
}): ComplexInvitationStatus {
|
||||||
|
if (invitation.acceptedAt) return 'ACCEPTED';
|
||||||
|
if (invitation.revokedAt) return 'REVOKED';
|
||||||
|
if (invitation.expiresAt < new Date()) return 'EXPIRED';
|
||||||
|
return 'PENDING';
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatInvitation(invitation: {
|
||||||
|
id: string;
|
||||||
|
complexId: string;
|
||||||
|
email: string;
|
||||||
|
expiresAt: Date;
|
||||||
|
acceptedAt: Date | null;
|
||||||
|
revokedAt: Date | null;
|
||||||
|
createdAt: Date;
|
||||||
|
}): ComplexInvitation {
|
||||||
|
return {
|
||||||
|
id: invitation.id,
|
||||||
|
complexId: invitation.complexId,
|
||||||
|
email: invitation.email,
|
||||||
|
status: getInvitationStatus(invitation),
|
||||||
|
createdAt: invitation.createdAt.toISOString(),
|
||||||
|
expiresAt: invitation.expiresAt.toISOString(),
|
||||||
|
acceptedAt: invitation.acceptedAt?.toISOString() ?? null,
|
||||||
|
revokedAt: invitation.revokedAt?.toISOString() ?? null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureComplexAdmin(userId: string, complexId: string) {
|
||||||
|
const membership = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!membership) {
|
||||||
|
throw new ComplexMembersError('No tenés acceso a este complejo.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (membership.role !== 'ADMIN') {
|
||||||
|
throw new ComplexMembersError('Solo un ADMIN puede administrar usuarios.', 403);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureComplexMember(userId: string, complexId: string) {
|
||||||
|
const membership = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!membership) {
|
||||||
|
throw new ComplexMembersError('No tenés acceso a este complejo.', 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
return membership;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sendInvitationEmail(input: {
|
||||||
|
complexName: string;
|
||||||
|
inviteeEmail: string;
|
||||||
|
token: string;
|
||||||
|
}) {
|
||||||
|
const inviteUrl = new URL('/invite', APP_BASE_URL);
|
||||||
|
inviteUrl.searchParams.set('inviteToken', input.token);
|
||||||
|
inviteUrl.searchParams.set('email', input.inviteeEmail);
|
||||||
|
|
||||||
|
await sendMail({
|
||||||
|
to: input.inviteeEmail,
|
||||||
|
subject: `Te invitaron a ${input.complexName}`,
|
||||||
|
text: `Te invitaron a sumarte a ${input.complexName}. Abrí este enlace para crear tu cuenta o iniciar sesión: ${inviteUrl.toString()}`,
|
||||||
|
html: `
|
||||||
|
<h2>Te invitaron a <strong>${input.complexName}</strong></h2>
|
||||||
|
<p>Hacé click en el siguiente enlace para crear tu cuenta o iniciar sesión y aceptar la invitación:</p>
|
||||||
|
<p><a href="${inviteUrl.toString()}">${inviteUrl.toString()}</a></p>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshPendingInvitation(invitationId: string) {
|
||||||
|
const token = createInvitationToken();
|
||||||
|
const tokenHash = hashToken(token);
|
||||||
|
const expiresAt = nowPlusDays(INVITATION_TTL_DAYS);
|
||||||
|
|
||||||
|
const invitation = await db.complexInvitation.update({
|
||||||
|
where: {
|
||||||
|
id: invitationId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
tokenHash,
|
||||||
|
expiresAt,
|
||||||
|
acceptedAt: null,
|
||||||
|
revokedAt: null,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
invitation,
|
||||||
|
token,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listComplexUsers(complexId: string) {
|
||||||
|
const [members, invitations] = await Promise.all([
|
||||||
|
db.complexUser.findMany({
|
||||||
|
where: { complexId },
|
||||||
|
include: {
|
||||||
|
user: true,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'asc',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
db.complexInvitation.findMany({
|
||||||
|
where: {
|
||||||
|
complexId,
|
||||||
|
acceptedAt: null,
|
||||||
|
revokedAt: null,
|
||||||
|
expiresAt: {
|
||||||
|
gte: new Date(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const membersWithAvatars = members.map((member) => ({
|
||||||
|
userId: member.userId,
|
||||||
|
fullName: member.user.name,
|
||||||
|
email: member.user.email,
|
||||||
|
avatarUrl: resolveAvatarUrl({
|
||||||
|
email: member.user.email,
|
||||||
|
image: member.user.image,
|
||||||
|
}),
|
||||||
|
role: member.role,
|
||||||
|
createdAt: member.createdAt.toISOString(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
members: membersWithAvatars,
|
||||||
|
invitations: invitations.map(formatInvitation),
|
||||||
|
} satisfies ComplexUsersOverview;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function inviteComplexUser(
|
||||||
|
userId: string,
|
||||||
|
complexId: string,
|
||||||
|
input: InviteComplexUserInput
|
||||||
|
): Promise<InviteComplexUserResponse> {
|
||||||
|
await ensureComplexAdmin(userId, complexId);
|
||||||
|
|
||||||
|
const email = normalizeEmail(input.email);
|
||||||
|
const complex = await db.complex.findUnique({
|
||||||
|
where: { id: complexId },
|
||||||
|
select: {
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!complex) {
|
||||||
|
throw new ComplexMembersError('El complejo no existe.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const existingUser = await db.user.findUnique({
|
||||||
|
where: { email },
|
||||||
|
select: { id: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingUser) {
|
||||||
|
const existingMembership = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId: existingUser.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: { userId: true },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingMembership) {
|
||||||
|
throw new ComplexMembersError('Ese usuario ya pertenece al complejo.', 409);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const token = createInvitationToken();
|
||||||
|
|
||||||
|
const invitation = await db.$transaction(async (tx) => {
|
||||||
|
const pendingInvitation = await tx.complexInvitation.findFirst({
|
||||||
|
where: {
|
||||||
|
complexId,
|
||||||
|
email,
|
||||||
|
acceptedAt: null,
|
||||||
|
revokedAt: null,
|
||||||
|
expiresAt: {
|
||||||
|
gte: new Date(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'desc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (pendingInvitation) {
|
||||||
|
const refreshed = await tx.complexInvitation.update({
|
||||||
|
where: {
|
||||||
|
id: pendingInvitation.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
tokenHash: hashToken(token),
|
||||||
|
expiresAt: nowPlusDays(INVITATION_TTL_DAYS),
|
||||||
|
acceptedAt: null,
|
||||||
|
revokedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return refreshed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.complexInvitation.create({
|
||||||
|
data: {
|
||||||
|
id: uuidv7(),
|
||||||
|
complexId,
|
||||||
|
email,
|
||||||
|
tokenHash: hashToken(token),
|
||||||
|
expiresAt: nowPlusDays(INVITATION_TTL_DAYS),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await sendInvitationEmail({
|
||||||
|
complexName: complex.complexName,
|
||||||
|
inviteeEmail: email,
|
||||||
|
token,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
invitation: formatInvitation(invitation),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function resendComplexInvitation(
|
||||||
|
userId: string,
|
||||||
|
complexId: string,
|
||||||
|
invitationId: string
|
||||||
|
): Promise<InviteComplexUserResponse> {
|
||||||
|
await ensureComplexAdmin(userId, complexId);
|
||||||
|
|
||||||
|
const pendingInvitation = await db.complexInvitation.findUnique({
|
||||||
|
where: {
|
||||||
|
id: invitationId,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
complex: {
|
||||||
|
select: {
|
||||||
|
complexName: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!pendingInvitation || pendingInvitation.complexId !== complexId) {
|
||||||
|
throw new ComplexMembersError('La invitación no existe.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingInvitation.acceptedAt) {
|
||||||
|
throw new ComplexMembersError('La invitación ya fue aceptada.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pendingInvitation.revokedAt) {
|
||||||
|
throw new ComplexMembersError('La invitación fue cancelada.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
const refreshed = await refreshPendingInvitation(invitationId);
|
||||||
|
|
||||||
|
await sendInvitationEmail({
|
||||||
|
complexName: refreshed.invitation.complex.complexName,
|
||||||
|
inviteeEmail: pendingInvitation.email,
|
||||||
|
token: refreshed.token,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
invitation: formatInvitation({
|
||||||
|
id: refreshed.invitation.id,
|
||||||
|
complexId: refreshed.invitation.complexId,
|
||||||
|
email: refreshed.invitation.email,
|
||||||
|
expiresAt: refreshed.invitation.expiresAt,
|
||||||
|
acceptedAt: refreshed.invitation.acceptedAt,
|
||||||
|
revokedAt: refreshed.invitation.revokedAt,
|
||||||
|
createdAt: refreshed.invitation.createdAt,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function acceptComplexInvitations(
|
||||||
|
userId: string,
|
||||||
|
email: string,
|
||||||
|
input: AcceptComplexInvitationsInput = {}
|
||||||
|
): Promise<AcceptComplexInvitationsResponse> {
|
||||||
|
const normalizedEmail = normalizeEmail(email);
|
||||||
|
const tokenHash = input.inviteToken ? hashToken(input.inviteToken) : null;
|
||||||
|
|
||||||
|
const invitations = await db.complexInvitation.findMany({
|
||||||
|
where: {
|
||||||
|
email: normalizedEmail,
|
||||||
|
acceptedAt: null,
|
||||||
|
revokedAt: null,
|
||||||
|
expiresAt: {
|
||||||
|
gte: new Date(),
|
||||||
|
},
|
||||||
|
...(tokenHash
|
||||||
|
? {
|
||||||
|
tokenHash,
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
createdAt: 'asc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tokenHash && invitations.length === 0) {
|
||||||
|
throw new ComplexMembersError('La invitación es inválida o ya venció.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
let acceptedCount = 0;
|
||||||
|
|
||||||
|
for (const invitation of invitations) {
|
||||||
|
await db.$transaction(async (tx) => {
|
||||||
|
const existingMembership = await tx.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId: invitation.complexId,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
userId: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!existingMembership) {
|
||||||
|
await tx.complexUser.create({
|
||||||
|
data: {
|
||||||
|
complexId: invitation.complexId,
|
||||||
|
userId,
|
||||||
|
role: 'EMPLOYEE',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await tx.complexInvitation.update({
|
||||||
|
where: {
|
||||||
|
id: invitation.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
acceptedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
acceptedCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
acceptedCount,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function revokeComplexUser(
|
||||||
|
userId: string,
|
||||||
|
complexId: string,
|
||||||
|
input: RevokeComplexUserInput
|
||||||
|
) {
|
||||||
|
await ensureComplexAdmin(userId, complexId);
|
||||||
|
|
||||||
|
const membership = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId: input.userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
role: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!membership) {
|
||||||
|
throw new ComplexMembersError('Ese usuario no pertenece al complejo.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (membership.role !== 'EMPLOYEE') {
|
||||||
|
throw new ComplexMembersError('Solo podés revocar usuarios con rol EMPLOYEE.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.complexUser.delete({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId: input.userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function cancelComplexInvitation(
|
||||||
|
userId: string,
|
||||||
|
complexId: string,
|
||||||
|
invitationId: string
|
||||||
|
): Promise<CancelComplexInvitationResponse> {
|
||||||
|
await ensureComplexAdmin(userId, complexId);
|
||||||
|
|
||||||
|
const invitation = await db.complexInvitation.findUnique({
|
||||||
|
where: {
|
||||||
|
id: invitationId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
complexId: true,
|
||||||
|
acceptedAt: true,
|
||||||
|
revokedAt: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!invitation || invitation.complexId !== complexId) {
|
||||||
|
throw new ComplexMembersError('La invitación no existe.', 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invitation.acceptedAt) {
|
||||||
|
throw new ComplexMembersError('La invitación ya fue aceptada.', 409);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invitation.revokedAt) {
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
await db.complexInvitation.update({
|
||||||
|
where: {
|
||||||
|
id: invitationId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
revokedAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getComplexUsersOverview(userId: string, complexId: string) {
|
||||||
|
await ensureComplexMember(userId, complexId);
|
||||||
|
return listComplexUsers(complexId);
|
||||||
|
}
|
||||||
@@ -1,21 +1,67 @@
|
|||||||
import { v7 as uuidv7 } from 'uuid'
|
import { Prisma } from '@/generated/prisma/client';
|
||||||
import { Prisma } from '@/generated/prisma/client'
|
import { db } from '@/lib/prisma';
|
||||||
import { db } from '@/lib/prisma'
|
import { v7 as uuidv7 } from 'uuid';
|
||||||
|
|
||||||
export type CreateComplexInput = {
|
export type CreateComplexInput = {
|
||||||
complexName: string
|
complexName: string;
|
||||||
physicalAddress: string
|
physicalAddress: string;
|
||||||
adminEmail: string
|
adminEmail: string;
|
||||||
planCode?: string
|
userId?: string;
|
||||||
|
planCode?: string;
|
||||||
|
city?: string;
|
||||||
|
state?: string;
|
||||||
|
country?: string;
|
||||||
|
setupCourts?: boolean;
|
||||||
|
courtSportId?: string;
|
||||||
|
courtStartTime?: string;
|
||||||
|
courtEndTime?: string;
|
||||||
|
courtDaysOfWeek?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type DayOfWeek = 'MONDAY' | 'TUESDAY' | 'WEDNESDAY' | 'THURSDAY' | 'FRIDAY' | 'SATURDAY' | 'SUNDAY';
|
||||||
|
|
||||||
|
function toMinutes(value: string): number {
|
||||||
|
const [hours, minutes] = value.split(':').map((part) => Number(part));
|
||||||
|
return hours * 60 + minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertAvailabilityRanges(
|
||||||
|
availability: Array<{
|
||||||
|
dayOfWeek: DayOfWeek;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
}>
|
||||||
|
) {
|
||||||
|
for (const range of availability) {
|
||||||
|
const start = toMinutes(range.startTime);
|
||||||
|
const end = toMinutes(range.endTime);
|
||||||
|
if (start >= end) {
|
||||||
|
throw new Error(`El rango ${range.startTime}-${range.endTime} es inválido.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureActiveSport(sportId: string) {
|
||||||
|
const sport = await db.sport.findFirst({
|
||||||
|
where: { id: sportId, isActive: true },
|
||||||
|
select: { id: true, name: true },
|
||||||
|
});
|
||||||
|
if (!sport) {
|
||||||
|
throw new Error('El deporte seleccionado no existe o está inactivo.');
|
||||||
|
}
|
||||||
|
return sport;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UpdateComplexInput = {
|
export type UpdateComplexInput = {
|
||||||
complexName?: string
|
complexName?: string;
|
||||||
physicalAddress?: string | null
|
physicalAddress?: string | null;
|
||||||
complexSlug?: string
|
complexSlug?: string;
|
||||||
adminEmail?: string
|
adminEmail?: string;
|
||||||
planCode?: string | null
|
planCode?: string | null;
|
||||||
}
|
city?: string | null;
|
||||||
|
state?: string | null;
|
||||||
|
country?: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
function slugify(value: string): string {
|
function slugify(value: string): string {
|
||||||
return value
|
return value
|
||||||
@@ -25,18 +71,15 @@ function slugify(value: string): string {
|
|||||||
.trim()
|
.trim()
|
||||||
.replace(/[^a-z0-9\s-]/g, '')
|
.replace(/[^a-z0-9\s-]/g, '')
|
||||||
.replace(/\s+/g, '-')
|
.replace(/\s+/g, '-')
|
||||||
.replace(/-+/g, '-')
|
.replace(/-+/g, '-');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildUniqueSlug(
|
async function buildUniqueSlug(source: string, excludeComplexId?: string): Promise<string> {
|
||||||
source: string,
|
const base = slugify(source);
|
||||||
excludeComplexId?: string,
|
const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`;
|
||||||
): Promise<string> {
|
|
||||||
const base = slugify(source)
|
|
||||||
const fallback = base.length > 0 ? base : `complex-${uuidv7().slice(0, 8)}`
|
|
||||||
|
|
||||||
let candidate = fallback
|
let candidate = fallback;
|
||||||
let index = 1
|
let index = 1;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
const existing = await db.complex.findFirst({
|
const existing = await db.complex.findFirst({
|
||||||
@@ -51,82 +94,190 @@ async function buildUniqueSlug(
|
|||||||
: {}),
|
: {}),
|
||||||
},
|
},
|
||||||
select: { id: true },
|
select: { id: true },
|
||||||
})
|
});
|
||||||
|
|
||||||
if (!existing) return candidate
|
if (!existing) return candidate;
|
||||||
|
|
||||||
index += 1
|
index += 1;
|
||||||
candidate = `${fallback}-${index}`
|
candidate = `${fallback}-${index}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createComplex(input: CreateComplexInput) {
|
export async function createComplex(input: CreateComplexInput) {
|
||||||
const complexSlug = await buildUniqueSlug(input.complexName)
|
const { userId } = input;
|
||||||
|
|
||||||
return db.complex.create({
|
return db.$transaction(async (tx) => {
|
||||||
|
const complexSlug = await buildUniqueSlug(input.complexName);
|
||||||
|
|
||||||
|
const complex = await tx.complex.create({
|
||||||
data: {
|
data: {
|
||||||
id: uuidv7(),
|
id: uuidv7(),
|
||||||
complexName: input.complexName,
|
complexName: input.complexName,
|
||||||
physicalAddress: input.physicalAddress.trim(),
|
physicalAddress: input.physicalAddress.trim(),
|
||||||
|
city: input.city?.trim() || null,
|
||||||
|
state: input.state?.trim() || null,
|
||||||
|
country: input.country?.trim() || null,
|
||||||
complexSlug,
|
complexSlug,
|
||||||
adminEmail: input.adminEmail,
|
adminEmail: input.adminEmail,
|
||||||
planCode: input.planCode,
|
planCode: input.planCode,
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
await tx.complexUser.create({
|
||||||
|
data: {
|
||||||
|
complexId: complex.id,
|
||||||
|
userId,
|
||||||
|
role: 'ADMIN',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.setupCourts && input.courtSportId) {
|
||||||
|
const sport = await ensureActiveSport(input.courtSportId);
|
||||||
|
const availability = (input.courtDaysOfWeek ?? []).map((day) => ({
|
||||||
|
dayOfWeek: day as DayOfWeek,
|
||||||
|
startTime: input.courtStartTime ?? '08:00',
|
||||||
|
endTime: input.courtEndTime ?? '22:00',
|
||||||
|
}));
|
||||||
|
|
||||||
|
assertAvailabilityRanges(availability);
|
||||||
|
|
||||||
|
const court = await tx.court.create({
|
||||||
|
data: {
|
||||||
|
id: uuidv7(),
|
||||||
|
complexId: complex.id,
|
||||||
|
sportId: input.courtSportId,
|
||||||
|
name: `${sport.name} 1`,
|
||||||
|
slotDurationMinutes: 60,
|
||||||
|
basePrice: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await tx.courtAvailability.createMany({
|
||||||
|
data: availability.map((avail) => ({
|
||||||
|
id: uuidv7(),
|
||||||
|
courtId: court.id,
|
||||||
|
dayOfWeek: avail.dayOfWeek,
|
||||||
|
startTime: avail.startTime,
|
||||||
|
endTime: avail.endTime,
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return complex;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getComplexById(id: string) {
|
export async function getComplexById(id: string) {
|
||||||
return db.complex.findUnique({
|
return db.complex.findUnique({
|
||||||
where: { id },
|
where: { id },
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getComplexBySlug(slug: string) {
|
export async function getComplexBySlug(slug: string) {
|
||||||
return db.complex.findUnique({
|
return db.complex.findUnique({
|
||||||
where: { complexSlug: slug },
|
where: { complexSlug: slug },
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listMyComplexes(appUserId: string) {
|
export async function listMyComplexes(userId: string) {
|
||||||
const complexUsers = await db.complexUser.findMany({
|
const complexUsers = await db.complexUser.findMany({
|
||||||
where: { userId: appUserId },
|
where: { userId },
|
||||||
include: {
|
include: {
|
||||||
complex: true,
|
complex: true,
|
||||||
},
|
},
|
||||||
orderBy: {
|
orderBy: {
|
||||||
createdAt: 'asc',
|
createdAt: 'asc',
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
return complexUsers.map((complexUser) => complexUser.complex)
|
return complexUsers.map((complexUser) => ({
|
||||||
|
...complexUser.complex,
|
||||||
|
role: complexUser.role,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCurrentComplex(userId: string, complexId: string) {
|
||||||
|
const complexUser = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
complex: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!complexUser) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...complexUser.complex,
|
||||||
|
role: complexUser.role,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function selectComplex(userId: string, complexId: string) {
|
||||||
|
const complexUser = await db.complexUser.findUnique({
|
||||||
|
where: {
|
||||||
|
complexId_userId: {
|
||||||
|
complexId,
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
complex: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!complexUser) return null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...complexUser.complex,
|
||||||
|
role: complexUser.role,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateComplex(id: string, input: UpdateComplexInput) {
|
export async function updateComplex(id: string, input: UpdateComplexInput) {
|
||||||
const data: Record<string, unknown> = {}
|
const data: Record<string, unknown> = {};
|
||||||
|
|
||||||
if (input.complexName) {
|
if (input.complexName) {
|
||||||
data.complexName = input.complexName
|
data.complexName = input.complexName;
|
||||||
data.complexSlug = await buildUniqueSlug(input.complexName, id)
|
data.complexSlug = await buildUniqueSlug(input.complexName, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.complexSlug) {
|
if (input.complexSlug) {
|
||||||
data.complexSlug = await buildUniqueSlug(input.complexSlug, id)
|
data.complexSlug = await buildUniqueSlug(input.complexSlug, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.adminEmail) {
|
if (input.adminEmail) {
|
||||||
data.adminEmail = input.adminEmail
|
data.adminEmail = input.adminEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.planCode !== undefined) {
|
if (input.planCode !== undefined) {
|
||||||
data.planCode = input.planCode
|
data.planCode = input.planCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.physicalAddress !== undefined) {
|
if (input.physicalAddress !== undefined) {
|
||||||
data.physicalAddress = input.physicalAddress?.trim() ?? null
|
data.physicalAddress = input.physicalAddress?.trim() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.city !== undefined) {
|
||||||
|
data.city = input.city?.trim() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.state !== undefined) {
|
||||||
|
data.state = input.state?.trim() ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.country !== undefined) {
|
||||||
|
data.country = input.country?.trim() ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.complex.update({
|
return db.complex.update({
|
||||||
where: { id },
|
where: { id },
|
||||||
data: data as Prisma.ComplexUncheckedUpdateInput,
|
data: data as Prisma.ComplexUncheckedUpdateInput,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { zValidator } from '@hono/zod-validator'
|
import { requireAuth } from '@/middlewares/require-auth.middleware';
|
||||||
import { createCourtSchema, updateCourtSchema } from '@repo/api-contract'
|
import { createCourtHandler } from '@/modules/court/handlers/create-court.handler';
|
||||||
import { Hono } from 'hono'
|
import { listCourtsByComplexHandler } from '@/modules/court/handlers/list-courts-by-complex.handler';
|
||||||
import { z } from 'zod'
|
import { updateCourtHandler } from '@/modules/court/handlers/update-court.handler';
|
||||||
import { requireAuth } from '@/middlewares/require-auth.middleware'
|
import type { AppEnv } from '@/types/hono';
|
||||||
import { createCourtHandler } from '@/modules/court/handlers/create-court.handler'
|
import { zValidator } from '@hono/zod-validator';
|
||||||
import { listCourtsByComplexHandler } from '@/modules/court/handlers/list-courts-by-complex.handler'
|
import { createCourtSchema, updateCourtSchema } from '@repo/api-contract';
|
||||||
import { updateCourtHandler } from '@/modules/court/handlers/update-court.handler'
|
import { Hono } from 'hono';
|
||||||
import type { AppEnv } from '@/types/hono'
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const courtRoutes = new Hono<AppEnv>()
|
export const courtRoutes = new Hono<AppEnv>();
|
||||||
const complexIdParamsSchema = z.object({ complexId: z.uuid() })
|
const complexIdParamsSchema = z.object({ complexId: z.uuid() });
|
||||||
const courtIdParamsSchema = z.object({ id: z.uuid() })
|
const courtIdParamsSchema = z.object({ id: z.uuid() });
|
||||||
|
|
||||||
courtRoutes.use('*', requireAuth)
|
courtRoutes.use('*', requireAuth);
|
||||||
|
|
||||||
courtRoutes.get(
|
courtRoutes.get(
|
||||||
'/complex/:complexId',
|
'/complex/:complexId',
|
||||||
zValidator('param', complexIdParamsSchema),
|
zValidator('param', complexIdParamsSchema),
|
||||||
listCourtsByComplexHandler,
|
listCourtsByComplexHandler
|
||||||
)
|
);
|
||||||
courtRoutes.post(
|
courtRoutes.post(
|
||||||
'/complex/:complexId',
|
'/complex/:complexId',
|
||||||
zValidator('param', complexIdParamsSchema),
|
zValidator('param', complexIdParamsSchema),
|
||||||
zValidator('json', createCourtSchema),
|
zValidator('json', createCourtSchema),
|
||||||
createCourtHandler,
|
createCourtHandler
|
||||||
)
|
);
|
||||||
courtRoutes.patch(
|
courtRoutes.patch(
|
||||||
'/:id',
|
'/:id',
|
||||||
zValidator('param', courtIdParamsSchema),
|
zValidator('param', courtIdParamsSchema),
|
||||||
zValidator('json', updateCourtSchema),
|
zValidator('json', updateCourtSchema),
|
||||||
updateCourtHandler,
|
updateCourtHandler
|
||||||
)
|
);
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import type { CreateCourtInput } from '@repo/api-contract'
|
import { CourtServiceError, createCourt } from '@/modules/court/services/court.service';
|
||||||
import { CourtServiceError, createCourt } from '@/modules/court/services/court.service'
|
import type { AppContext } from '@/types/hono';
|
||||||
import type { AppContext } from '@/types/hono'
|
import type { CreateCourtInput } from '@repo/api-contract';
|
||||||
|
|
||||||
type ComplexIdParams = { complexId: string }
|
type ComplexIdParams = { complexId: string };
|
||||||
|
|
||||||
export async function createCourtHandler(c: AppContext) {
|
export async function createCourtHandler(c: AppContext) {
|
||||||
const { complexId } = c.req.valid('param' as never) as ComplexIdParams
|
const { complexId } = c.req.valid('param' as never) as ComplexIdParams;
|
||||||
const payload = c.req.valid('json' as never) as CreateCourtInput
|
const payload = c.req.valid('json' as never) as CreateCourtInput;
|
||||||
const appUserId = c.get('appUserId')
|
const user = c.get('user');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const court = await createCourt(appUserId, complexId, payload)
|
const court = await createCourt(user.id, complexId, payload);
|
||||||
return c.json(court, 201)
|
return c.json(court, 201);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof CourtServiceError) {
|
if (error instanceof CourtServiceError) {
|
||||||
return c.json({ message: error.message }, error.status)
|
return c.json({ message: error.message }, error.status);
|
||||||
}
|
}
|
||||||
throw error
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user