diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603145258_d62464bacee14183959c11a8be8b7c91.trn b/.appdata/.dotnet/TelemetryStorageService/20260603145258_d62464bacee14183959c11a8be8b7c91.trn deleted file mode 100644 index 24001f9..0000000 --- a/.appdata/.dotnet/TelemetryStorageService/20260603145258_d62464bacee14183959c11a8be8b7c91.trn +++ /dev/null @@ -1,5 +0,0 @@ -https://dc.services.visualstudio.com/v2/track -Content-Type:application/x-json-stream -Content-Encoding:gzip - -H4sIAAAAAAAACoxUS2/bOBD+KwLPpkJRFCUK2IPhtFgjTWMk7u5ibxQ5jInIpEFRToMi/31BWa6dooe9CaN5fY/hD+TkHlCLlofDpyO4OKAFinYKUUI5JhyTcluwtqJt1eSiFpyU7F+0QPYO3lCLaqZUoQTgEjhgpkmFu9IorDWIghSa0IqlnvJ5QO0PJG2u4WgV5H74C8JgvUMt+ts67V/TbGlz1ftR58H3sHZDlE6lZb6uV3f4y3KzfdicsgYYUnFuNWqRrlmheSOwMlRgxiTBHaEGC6U1F6STpjOnMusiBCf7fNAvl/naRwdRtTSnZU4wFeh9gbSMMq3cyQG2b4e0xUTRbYovpvD02f5ARwiopYszmad2N6q3N/uhG22vb6IMzxBNkHt49eEFjrJHC3QI/gAhWpi4eXjKLisVJCc55ZQQtED3Uu2sg2x9mz30CbBUDWGgSyzAaMxEIbFgZYl1WcmaclPXNAG+g+Cgv+p6b1XwgzcxmynPPsxZeRetG/04ZGsX4TnIeKr7LPsB0AJtxq63w+4RpH7b+scx/XNjn7CsxhDAxWwj4y77Uw471CIhG1VIIYWUXCkDFQFDNJGc1Bq45qAZ1bVmgmnOBFGcya5gWlZFZ1IELdDDGA9jPPP//WqJbbD7PejLBtuJ400vo/Fhv9bgojU2SXPOmAzV9xOqbO656i24+IFk1CJSANVlA9jQgmPWaIllVQrMeckNFErUtDgRZuzz+JMnYoxmpamahneiKwhQUzeM1KYjWplSCV11UNOa1A00QhHSVaxoOqGIhqJiOqELXo8q/mqFchLocXTpOrN1gv1qHf7O2YWRTfDRK98nwWwPT29DhP3vVFv5/cEPNib8Zpb22wD33qW/8/3PnM3FSx8vMUh3kE2nR6uOUMI4po0ymOma4g4kwaKsGK8bIAAKLdCtVy8QsmQwad2kydlTD0/ZMqidjaDiGNLkfyZQJzk/n0/mwkf+9dN25QMsD4fFHP3jmEg6NTsb4MO78m1I6Ts/xCvIM7Qn6M15sSs7aTieniqbglzrBjQvMGVFhZkQBW5EVeOubjrR8QYqdbFr9gjaBlBx6rcNY5o2M/s7Xy5DtEaqOKTj+eLVZKfZn3PKr0teyfaz+jT8SrrZSnOngiTLPp0ezslB/+vdfH9/f/8PAAD//wMABJt80ygGAAA= \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603145308_5500dec2151042f69b2acc6603bae493.trn b/.appdata/.dotnet/TelemetryStorageService/20260603145308_5500dec2151042f69b2acc6603bae493.trn deleted file mode 100644 index 8af06a0..0000000 --- a/.appdata/.dotnet/TelemetryStorageService/20260603145308_5500dec2151042f69b2acc6603bae493.trn +++ /dev/null @@ -1,5 +0,0 @@ -https://dc.services.visualstudio.com/v2/track -Content-Type:application/x-json-stream -Content-Encoding:gzip - -H4sIAAAAAAAACpSUwW7cNhCGX0WYsyiTEkWJuhnrBF24qRfJIi16ozijmLBMLUjKiWHsuxfUbpD6VPRCECPO8J9vfuoNvHkmGOD2dPrwQj5FKCG5LVTzWjGuGG+OQg5tM/C+kk0tRdv/DSW4e3qFATpprbCaWEOKmETesrGZLEMkLbhAXrcy1zTfIgxvYFyF9OIsVUv8SiG6xcMAfzqPy/d8t3GVnZcVq7DMtPcxGW+zmD/2u3v2++3h+HC4nIoUc3LlEAbATgpUvWZ2qjWT0nA28npi2iIqzUczjdMlzflEwZu5ivj0635ckqdkh7qqm4qzWsO5BDTJZMmjiXR8PWUVG6K7HC+38LYd3uCFAgx1+RPmpdyNnd3NcxxXN+PNZU0mPsVtnYxNS3iFEk5hOVFIjjY+tzHS8zi/Hk18+ng5k7fxww+yayLcLatPMIBoOihhAzTPJrnFF1eNu9mRT9vHFJyPzv5XrQZKePhSHGaTpiU8v5vHlwvm/f+g/MnYR+ep2N/BAFxQjU1PbKqFYrJHw0zbaKZUoyYSVne1gBJ2i0/Or8sai71P9C1sLcEAH80c6aLvNthHl8imNeQ+/1LZV4ew4GpT8WuWgle8ajjPSWs6ran4TOgC2US5iWNYr/W+vk+pVb0l7dYQyKfiYNJj8ZuJjzCANr0VRhttjLJ2opbTxJEbxTskhYpQ1tih1BKV1NwqaUYh0bRinHIESqBsnWJz69hMulNSsNaaiUkpiBmFxPpOj42Smqwc33EsHuacZ2zPJWHDNE3IpBaGadk0DJvWdLWauq7O/H8iufpB8AwY6eXy7jYFCrEnVILl18yk1oL1uu3Y2PWjHlVPrc2a7yl4mv8F6pOzYYnLlIqrQ4p36D6vPv87is0t351nP7YZ3S32iUKRZ2ycz0/lOtbz+Xz+BwAA//8DAK3VVNOCBAAA \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603145308_8ac143cd27d54693ab5c4e3150472e22.trn b/.appdata/.dotnet/TelemetryStorageService/20260603145308_8ac143cd27d54693ab5c4e3150472e22.trn deleted file mode 100644 index a8262d4..0000000 --- a/.appdata/.dotnet/TelemetryStorageService/20260603145308_8ac143cd27d54693ab5c4e3150472e22.trn +++ /dev/null @@ -1,5 +0,0 @@ -https://dc.services.visualstudio.com/v2/track -Content-Type:application/x-json-stream -Content-Encoding:gzip - -H4sIAAAAAAAACpSTQW/cNhCF/4owZ1EmKYoSdTPWKbpwEy+SRVr0RnFGMWGZWlCUk8DY/x5Qu4GT3noRhAEf571vhq8Q7DNBD7en07sXCmmBEpLfSpJLzbhmvD4K1Td1z7tK1Uoa3v4LJfh7+g49tMo54QyxmjQxhbxhQz06hkhGcIFcNirfab8s0L+C9RXSi3dUzctnioufA/Twtw84f829ra/cNK9YxXmifViSDS6b+bDf3bO/bg/Hh8Pl1EJLFlceoQdslUDdGeZGaZhSlrOBy5EZh6gNH+w4jBeZD4lisFO14NNbf5xToOR6Wcm64kwaOJeANtlsebALHb+fsosN0V2ul1t5++1f4YUi9LL8CfNy3Y2b/M3zMqx+wpvLN9nlKac8xflEMXnamGwxp8kmP4fi2mk3eQoJSnj4VBwmm8Y5Pv8G6tMl//5/xD/m7u++kVsT4W5eQ4IehOJQwnvrHn2gYn8HPXBBEuuO2CiFZqpDy2xTG6Z1rUcSzrRSQAm7OSQf1nldin1I9CVuAaCHP+y00MX6bXSPPpFLa8yp/tF5Fw5xxtWl4o2/4BWvap6dPKzptKbiI6GP5BLlfMe4Xu/7j0RquYl2a4wUUnGw6bH40y6P0IOxnRPWWGOtdm6khtPIkVvNWySNmlBJbFEZhVoZ7rSyg1BoGzGMuQIlUB53sW2YwIGjVophbTRTQ9My46xkrtV80AYFbYjfOBYPU9ZZ13FFWDNDIzJlhGVG1TXDurGt1GPbyqz7ieQ6fcEzYKSXy1vZHGjEjlALJpVomDJGsM40LRvabjCD7qhx2fM9xUDTL6DeexfnZR5TcV2e4jd0H9eQ33uxLdJXH9i3bUZ3s3uiWOQZWx/yel/Hej6fzz8AAAD//wMAtGYnyDYEAAA= \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603145308_b9debcf3f5ee4536bd636f1e091a712f.trn b/.appdata/.dotnet/TelemetryStorageService/20260603145308_b9debcf3f5ee4536bd636f1e091a712f.trn deleted file mode 100644 index 40b4084..0000000 --- a/.appdata/.dotnet/TelemetryStorageService/20260603145308_b9debcf3f5ee4536bd636f1e091a712f.trn +++ /dev/null @@ -1,5 +0,0 @@ -https://dc.services.visualstudio.com/v2/track -Content-Type:application/x-json-stream -Content-Encoding:gzip - -H4sIAAAAAAAACpRTwY7cKBD9FYuz8QDG2Pg26tlVWpPstJJRstobpoo0igdaGE8SjfrfV7g7Sia33NCDV/XqveKFBPOEZCS3p9NfzxjyQmqS/QYJJhRlirL2kcuxa0c2NLLVnZTyP1ITf4/fyUh6aS23GmmLCqkE1tGpdZYCoOaMAxOdLDXN54WML8T4BvDZW2zi8hHT4mMgI/nkA8SvpbfxjZ3jCk2KM+7Dkk2wRcw/+909fXt7eHw4XF4tuBRy44GMBHrJQQ2aWic0ldIwOjHhqLYASrPJuMldaD5kTMHMzQJffvaHmANmO4pGtA2jQpNzTcBkUyRPZsHH76eiYrPoruD1Bm/H8YU8YyKjqH+YeSl3Y2d/Y+PTkwlw43zwy5HU5JTiCVP2uNmxTTjPJvsYqmuT3ewxZFKThw/VYTbZxfT0yqMPl9H3fzD5O2OPPmC1vyMjYRwFtANSJ7iicgBDTddqqlSrHHKre8FJTXYxZB/WuC7VPmT8nDaVZCR/m3nBi77bZI8+o81rKtL/VSVr/ObzLkIBGKnJIUVYba5+2s1Zw5qWlcuHNZ/WXL1H8AltxjLTY1qv5X+jCCU20m5NCUOuDiYfqzdmOZKRaDNYbrTRxihrHXYMHQNmFOsBFSgEKaAHqSUoqZlV0kxcgun45ApSdJd0q22hBqE4KieoGGRLpRWc6nZCOvUd4tA5KUT3ytbqYS48YwcmEVqq0QGVmhuqZdtSaDvTC+X6XpQ4flhyTZyz4jfg8+VrbAoUwICgOBWSd1Rqzemgu55O/TDpSQ3Y2aL5HlPA+Rej3nmb4hJdrq4LU72y7v0ayveutuX56gP9tkV2F+0XTFWJ3PhQtvma8vl8Pv8PAAD//wMAZYD86yUEAAA= \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603145308_dfd35309f1174302ac99694cd6e6aa4b.trn b/.appdata/.dotnet/TelemetryStorageService/20260603145308_dfd35309f1174302ac99694cd6e6aa4b.trn deleted file mode 100644 index dd69150..0000000 --- a/.appdata/.dotnet/TelemetryStorageService/20260603145308_dfd35309f1174302ac99694cd6e6aa4b.trn +++ /dev/null @@ -1,5 +0,0 @@ -https://dc.services.visualstudio.com/v2/track -Content-Type:application/x-json-stream -Content-Encoding:gzip - -H4sIAAAAAAAACoxUXW/sJhD9KxavXRzAGBu/pZtcdZUmWd1dtVXfBhgnNA5eYZzcqyj/vcKb3HxcVeqLZQ0w58yZM/NEAtwj6cjp4XD+gCFNZEWSX0KCCUWZoqzac9nVVcfaUlZKK9X8TVbEX+B30pFGWsutRlqhQiodq6mpekudQ80Zd0zUMueEm4l0TwR86fDBWyzH6Q+Mkx8D6cifPrjxMWODL+0wzq6M44CbMCUINpO52qwv6O+n2/319nhrwik/Lr0jHXGN5E61mtpeaColMGqY6Km2zinNDPSmPz7zIWEMMJSTu3vDd2MKmGwnSlGVjApNnlfEQYJM2cCE+++HzGKR6CzHV0t4+e2eyANG0onVq5jHdCd28Cf3k5n94E6WL1mRQxwPGJPHRY3rXfFGgrOSlUIJxsiKXIK99QGLzVlxPeQSwbZMoquoxt5RqTlQLauKuqqGRqi+aUQu8QJjwOFd1ktv4ziNfSpeRC4+4OxO1+cBzIAZ4wsME5IV2Uy7BMHBMAY8/4Z2Tsdc+zjn4/UYkg/zOE/FJiS8ifBy/vp+PceIIRVbSLfFbzDdko5oaC0HDRpAWdtjzbBnjoFijUPlFDopXOOklk5JzaySYLh0UHPT5whZkV+zhrvZWpymNzaLSYZh4VC89Gk9eAzpg4ykI4yjcFWLtBdcUdk6oFBXmipVqR651Y3grzB7iDeYSEf61hjDrDVCam5BaKhczyqnei1E04Dsq7qRQqmaNWBd0wguXdWghaqvWCvIimzj6GabPre6WhqwjeM/aFOWinREsbpuLRgjlOQt6BYlV7JVGhste2CysgZ43YIxnEHNrG6FBa40SOPq/odK5+HGBzzz02GA7+9w21KV1S8WoOU9yE/Xv0S4x8cx3l0dbVxene/JinydQ94IxSZb5NEH+k3lib6ch+T3txHBobscHf7sI8zjUiwTKpVrNUpOeyF6KoWoKZjWUGF45ZwxTOvM5my0dxiL7DDwIU/Vj2TXu+I02luf0KY5Zn5/LTyud8V2gNSP8f7DJnlX2GcBSlFxljvj8OG4jRaOyrkWneJUSF5TqTWnra4baprWaKNarG3meD2nw5yKr+h8RJuWil+8uGCub9He/SzGqwteLMpZ9truuMUWaf/XEntekXuEaY54v2zr7olsQsC4IJ/Nx1HchEs/DH5COwY3kY4zplmpdatfKP73Ra5ZqVQjn5+fn/8FAAD//wMAY5ugbB0GAAA= \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205048_85c6cfaf868f4598a3bc02d5c1789234.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205048_85c6cfaf868f4598a3bc02d5c1789234.trn new file mode 100644 index 0000000..c5dd410 --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205048_85c6cfaf868f4598a3bc02d5c1789234.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACoxU227cNhD9FYGvFRXeRJF6c20HXaSOjeyiLfo2Ikc2a5laUJSTwPC/F9TauaJAXwRhSM45c+bMPJEID0h6cnY8Xj5izAupSQ5bSDChKdOUyYNgfct6ZZqOiVZI/TepSXiHn0lPOuUcdxapRI1UedbSQY6Oeo+WM+6ZaFXJCbcL6Z8IhMbjY3DYzMsfmJYwR9KTP0P088eCDaFx07z6Js0T7uKSIbpC5v3u/B39/ezmcH1zurXgUh43wZOeuNGC9+1InWGaKi4dNc4Jav2gZQuWeV9IQGhCzJgiTM3i77/i+zlHzK4XjZANo8KS55p4yFAoD7Dg4fOxsNgkuijxegtvv/0TecREelG/inlK98ZN4c3DMqxh8m+2L6nJMc1HTDngpsb1vvpKgrOGNUILxkhNrsDdhYjV7qK6nkqJ4AxT6CW1OHqqLAdqlZTUyxY6oceuEyOpyTtMEadvsl4Fl+ZlHnP1InL1Hc7+7PwywjBhwXgL04KkJrtlnyF6mOaIl5/QrfmU65DWcnw+xxziOq9LtYsZbxO8nL++P19TwpirG8h31W+w3JGeWDCOgwULoJ0bsWU4Ms9As86j9hq9Er7zyiqvlWVOKxi48tDyYSwRUpNfi4b71Tlclq9sNpNM08aheunT+RQw5u9kJD1hHIWXBukouKbKeKDQSku1lnpE7mwn+CvMAdItZtKT0QzDwJwbhLLcgbAg/cik16MVoutAjbLtlNC6ZR0433WCKy87dCBHyYwgNblJs19d/rHVcmvATZr/QZeLVKQnmrWtcTAMQituwBpUXCujLXZWjcCUdAPw1sAwcAYtc9YIB1xbUINvxy8qXcbbEPEiLMcJPn+DaxrdyF8cgOEjqB+uv03wgB/ndP/+ZOPm/eWB1OTDGstGqHbFIh9DpJ90GaardcrhcJcQPPqr2ePPPsIyLtU2odC6jolBUtWagapxRGqE41Sq0QzauRZ4eXExu3tMVXEYhFim6kuy6311ltxdyOjymgq/vzYe1/vqZoI8zunhu03yTWE/CtAIyVnpjMfH0zbaOGrvDXrNqVC8pcpaTo1tOzp0ZrCDNti6otj1mo9rrj6gDwld3ip+8eKGeX6H7v5nMV5d8GJRzorX9qcttkn7v5bYc00eEJY14cO2rfsnsosR04Z8sZ5GcRevwjSFBd0c/UJ627W8kZK/EPzPa6Y1jVTCPD8/P/8LAAD//wMA2D18ZxoGAAA= \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205048_e4580ca370e24b6182765c9246db5c34.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205048_e4580ca370e24b6182765c9246db5c34.trn new file mode 100644 index 0000000..95bd8e6 --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205048_e4580ca370e24b6182765c9246db5c34.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACpSTwW7cOAyGX8Xg2XIkWZYt34JJFzvIthm0g+5ib7JIN0IceSDLaYtg3r2QZ4o2e9uLIBAi+f8fqVcI9pmgh9vT6d0LhbRACclvIcmlZlwzXh8l7xveq67SxnSqFv9CCf6evkMPrXJOOEOsJk1MIW/YUI+OIZIRXCCXjco17ZcF+lewvkJ68Y6qeflMcfFzgB7+9gHnr7m39ZWb5hWrOE+0D0uywWUxH/a7e/bX7eH4cLi8WmjJyZVH6MGNxiI2I3Md10yJ2rHOOckMDrpurOGIWYT1lQ+JYrBTteDTr/44p0DJ9bKSdcWZNHAuAW2yWfJgFzp+P2UVG6K7HC+38HbtX+GFIvSy/AnzUu7GTf7meRlWP+HN5Ux2ecouT3E+UUyeNiabzWmyyc+huHbaTZ5CghIePhWHyaZxjs9vQH26+N//D/vH3P3dN3JrItzNa0jQg1AcSnhv3aMPVOzvoAcuSGLdERul0Ex1aJltasO0rvVIwplWCihhN4fkwzqvS7EPib7EzQD08IedFrpIv43u0SdyaY3Z1T866zjEGVeXil/8Ba94VfOs5GFNpzUVHwl9JJco+zvG9VrvPylSyy1pt8ZIIRUHmx6LP+3yCD0Y2zlhjTXWaudGajiNHLnVvEXSqAmVxBaVUaiV4U4rOwiFthHDmCNQAuVxF9uGDW0nBFeOjdIZpnCUbGikYGpQTav1MNa2ecOxeJhynnUdV4Q1MzQiU0ZYZlRdM6wb20o9tq0cf0Nynb7gGTDSy+WvbAo0YkeoBZNKNEwZI1hnmpYNbTeYQXfUuKz5nmKg6TdQ772L8zKPqbguT/EG3cc15P9ebIv01Qf2bZvR3eyeKBZ5xtaHvN7XsZ7P5/MPAAAA//8DAK6M6YU2BAAA \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205213_1de44ba5ee8240c19b45f01bd0504f7b.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205213_1de44ba5ee8240c19b45f01bd0504f7b.trn new file mode 100644 index 0000000..c4e40a9 --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205213_1de44ba5ee8240c19b45f01bd0504f7b.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACpSU227cNhCGX0XgtSjzJB50Z9gt6rqpF/EiLXpHcUa2EC21oKhNA2PfPaB2g8S9650w0D+Hb/7hG4n+gKQjt8fjLyeMeSE1yeMWEkxoyjRlci9Y14qOy8ZI4ZTR/5CajI/4lXTEqBB4cEglaqQKWEt7OQQKgI4zDky0quT0Lwvp3ogfG8DTGLCZl0+YlnGOpCN/jRHmL6W2H5swzSs0aZ7wIS7Zx1Ca+fPh7pH+cbvbP+0ufy24FHEzAukICwi8B00HGThVjvXUOQsUFPPGuNahMBfZGDOm6Kdmgc8/6sOcI+bQiUbIhlHhyLkm4LMvLfd+wf3XY+liQ3Rf4vUW3j67N3LCRDpRf4d5SXcTpvEmz8cJTzgdfVow3YT5cPARSE2OaT5iyiNuWLZJp8nncY7VtdjdNGLMpCZPz9Vu8nmY0+Edq+cLgof/QeCEqScdkayV2hhUUkrvdC8YgtFBa9EHz8zQC+QBWeuUcdwwLp1gTrbaWAOeuUBq8jLNvZ9+XzZ+TrDeAjNCC1TohNLaBYbS4tAaZOC17rUNDqQ1Qlmw0jvvuevRBKdDMcgHH17HiNXDfZmGowBpkQ6Ca6oseOpb6ajWUg/IgzOCk5rczTGPcZ3XpXqIGV/Sxo905Fc/LXghd5vC65gx5DUVqH/rUmyXZlhDrn44gLOGNZKxIlrzcc3VR4QxYchY8O7Tes33H4nQYhPdrSlhzNXO59fqN7+8FireBu5dGVWHMGDLcGDAvGYGUINGUAIMKKdAK8eCVr7nCnzL+6FESE2wGK7aPD4w5wUoQyXDlipoLbVCIRUtSik1NyrYdxyrp6nofLBMIUjqcACqHPfUKSkpyNYboQdjxPATkqv5OCuAAU+Xa9060AAWQXMqFG+pco5T61pDe2N712uLbSg9P2KKOP0E6sMY0rzMQ66u3q3eofu4xvLiVJuPv4yR/rvt6H4OnzFVZcd+jOXArms91+SAflkTHrYHq3sju3Jd1X57t4RthDK6Js/Zp7wer2HpGqWZOZ/P528AAAD//wMAJUGrCvUEAAA= \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205213_6a1585d3eef84177a71c857ded4af279.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205213_6a1585d3eef84177a71c857ded4af279.trn new file mode 100644 index 0000000..21bc8a3 --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205213_6a1585d3eef84177a71c857ded4af279.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACpSUTW/cNhCG/4rAsyjzS6Som2G36MJNvYgXbdAbxRnFRLTUgqI2DYz97wW1WyTuLTdhoJl555mX80aiOyLpyf3p9MsZY15ITXLYQoIJTZmmTB4E61vRc9mYjjHD9N+kJuEJv5GeGOU99xapRI1UAWvpIEdPAdByxoGJVpWa7vNC+jfiQgN4Dh6befkT0xLmSHryV4gwfy29XWj8NK/QpHnCXVyyi76I+WP38ER/v98fnvfXvxZcSnITgPSEeQQ+gKaj9JwqywZqbQcUFHPG2NaiMNe0EDOm6KZmgS/f+8OcI2bfi0bIhlFhyaUm4LIrkge34OHbqajYED2WeL2Ft8/+jZwxkV7U/8G8lrvzU7hb1mHCM04nlxZMd34+Hl0EUpNTmk+YcsANyxnTMC8hF6LeG9d1nEPnjGoNdLIV0oBkCB0zLViwTis9Ct+2IwydNWpUILgCjUoNZdAN3DS5HOZY3bQ/TAFjJjV5fqn2k8vjnI7v0L9cie5+AmiRTXoiWSu1MaiklM7qQTAEo73WYvCOmXEQyD2y1ipjuWFcWsGsbLXpDDhmPanJB+dfQ8Rq91i6cxQgO6Sj4JqqDhx1rbRUa6lH5N4awUlNHuaYQ1zndal2MePntM1LevKrmxa8Tnqf/GvI6POaCoRPuphxn2ZYfa6+G4CzhjWSsZK05tOaq48IIaHPWHAc0nqr978UocWW9LCmhDFXe5dfq9/c8kp6Yl3nubPOOqe9H7FlODJgTjMDqEEjKAEGlFWglWVeKzdwBa7lw1gipCZY/FZtFh8QmJNS0VEwQxVrgdrRSOql6vzoQIyGveNYPU8lz/mOKQRJLY5AleWOWiUlBdk6I/RojBh/QHIzC2cFMOD5+lg3BRqgQ9CcCsVbqqzltLOtoYPpBjvoDltfND9hijj9AOpD8Gle5jFXN69V79B9XGM5ONXmu68h0n+2HT3O/gumquzYhVje122tl5oc0S1rwuN2r/o3si+PqzpsZ0t0jVBG1+Qlu5TX0y0sbaM0M5fL5fIvAAAA//8DAKi9M8D0BAAA \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205213_9d1364f3c6814b01b37746855f7160cb.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205213_9d1364f3c6814b01b37746855f7160cb.trn new file mode 100644 index 0000000..4a492fe --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205213_9d1364f3c6814b01b37746855f7160cb.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACpSUXW/jKBSG/4rFtXEBY2x81013tFGn02ianV3tHYbjFNWBCHBnqyr/fYSTqumM9mLvrGPe8/GcF16RU3tAPbo+HH5/BpciKlGyS4gRJjARmNRbRvqG9bSuOlqTtqH/oBLZW3hBPWq51lRLwDUIwNyQBg/1qLExICmhhrCG55xqF1H/ipStDDxbDZWP3yBE6x3q0V/WGf8911a20pOfTRX8BGsXk3I6N/NlvbrFn6832/vN6VSEmMWVNahHRIOhgxF4rDXFXJIBS9kZbDhRbSsbCaw9yaxLEJyaqmie3usbnxwk3bOK1RXBTKJjiYxKKrc8qAjbl0PuYkF0k+PlEl4++1f0DAH1rHyDeUp3pSd7tY/DbCdzNfndzrrdyrvR7uagUi5cokPwBwjJwgLnN+tUePnsd7ucD31SUwRUoi2EvXVqOv24gVHNU3rwc1jIRPP0X2dQj9ScPCrRQnKalrLFeZjVZMGlX7QXhe8fis2k0ujD/sOSHk7s1/8D/Z3Sj9ZBsb7JGgrM1B3gkVGBeWcUVk0tsRC1GIFq2TKKSrTyLlk3+zkWa5dgd6b2ob/roB9tAp3mkGf6W2SzbYI3s07F+4IpqUhVE5JFczrMqfgKxgbQCfIQ2zCf8/0kYYItok92gl/wrOYQwKVio9Jj8YeKj6hHUnWaKqmkUkLrERoCIzFECdIaEEaA4cy0hktuBJdEC64Gyo1q6DDmCCoRZJMVi69H08qRK4EJAYY5yA4rLmoMAxsbomhXM/IBbnE/ZZ3SHeFgaixhNJhLqrDkdY1N3aiWibFt2XjB6WwISjL1SxP+GcGcvfTlZO232Q08n+7x0qcwpgMjKGacNphLSXEnmxYPbTfIQXTQ6DzZyrvo30B+gzD4aFN+RO6ss3s1/XzkfTO3EBxMF9u5szr46MdUnG1ZfNjX19nlV6xYLPrdOvzvYowbr58gFNlYyrqLZR6Px+MPAAAA//8DAHv/g4QMBQAA \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205214_46622c317ec243b19afc1bfe8ae82673.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205214_46622c317ec243b19afc1bfe8ae82673.trn new file mode 100644 index 0000000..1f9bc34 --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205214_46622c317ec243b19afc1bfe8ae82673.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACpRU0W7cKBT9FYtn4wDG2PgtmrTaUbabUTvqrvYNc68bFAdGgNNG0fz7Cs+s2jyt9gWha87l3HMOfiPePCMZye3p9OEFfU6kJtltJcGEokxR1h4FGzsxctkMsheKq79JTdw9vpKR9NJabjXSFhVSCayjUztbCoCaMw5MdLL0NN8SGd+IcQ3gi7PYhPQVY3LBk5H86TyE7+Vu4xq7hBWaGBbc+5SNt4XMH/vdPf399nB8OFxOJUwF3DggI2EWgU+g6NxaTqVmE9V6AAqSmb7XnUbRX2DOZ4zeLE2Cp5/3Q8gesx1FI9qGUaHJuSZgsimUJ5Pw+HoqLDaJ7kq93srbdnwjLxjJKOp/xby0u7GLu3lO0+oWuLms2aSntK2zsTnEV1KTUwwnjNnhps9tSvg8La9Hk54+Xs6UbfrwA+2aEXZh9ZmMhLcdqckm0LKY7IKvrhx3i0Oft485Op+c/a9eLanJw5fqsJg8h/j8zo8vF5n3/0PlT8Y+Oo/V/q5gOApoB6Sz4IrKAQw1XaupUq2akVvdC05qsgs+O7+GNVV7n/Fb3EYiI/loloQXfrfRPrqMNq+xzPmXKrk6xACrzdVPLzlrWNMyVkBrPq25+ozgItqMZYhjXK/9vr6HCCU20G6NEX2uDiY/Vr+Z9EhGos1gudFGG6OsnbFjODNgRrEeUIFCkAJ6kFqCkppZJc3EJZiOT3OpkJpgiU61pVXOOEvVWipkK6nkglPDUFGrJ637iTOtxDsdq4el4IwdmERoqcYZqNTcUC3blkLbmV6oue/F/Isk1zxwVgQGfLm8u42BAhgQFKdC8o5KrTkddNfTqR8mPakBO1s432P0uPwi1CdnY0hhztU1IdU76T6vvvw7qi0t352nPzaP7oJ9wlgVj43z5alcbT2fz+d/AAAA//8DAHLELVyCBAAA \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205214_4c5eb49e92964a9aac8965680e727562.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205214_4c5eb49e92964a9aac8965680e727562.trn new file mode 100644 index 0000000..831f90d --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205214_4c5eb49e92964a9aac8965680e727562.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACoxUS2/bOBD+KwLPpkJSFCUK2IPhtFgjTWMk7u5ibxQ5jInIokFRToMi/31BWa6dooe9CaN5fY/hD9SrPaAGLQ+HT0fo44AWKLopxAgTmAhMii0jTckaynPGikKw8l+0QO4O3lCDKq411RJwAQIwN6TEbWE1NgYkJdQQVvLUUz0PqPmBlMsNHJ2G3A9/QRic71GD/na98a9ptnK57vxo8uA7WPdDVL1Oy3xdr+7wl+Vm+7A5ZQ0wpOLcGdQgosHQ1ghsC00xl6TFUtYGG05UVclSAqtOZa6PEHrV5YN5ucw3PvYQdcNyVuQEM4neF8ioqNLKrRpg+3ZIW0wU3ab4YgpPn80PdISAGrY4k3lqd6M7d7Mf2tF15iaq8AzRBrWHVx9e4Kg6tECH4A8QooOJm4en7LISJTnJmWCEoAW6V3rnesjWt9lDlwArXRMOpsASrMFcUoUlLwpsilJVTNiqYhYt0B2EHrqrrvdOBz94G7OZ8uzDnJXvo+tHPw7Zuo/wHFQ81X1W3QBogTZj27lh9wjKvG3945j+9WOXsKzGEKCP2UbFXfanGnaoQVLVmiqppFJCawslAUsMUYJUBoQRYDgzleGSG8El0YKrlnKjStraFEEL9DDGwxjP/H+/WmIb3H4P5rLBduJ406lofdivDfTRWZekOWdMhuq6CVU291x1Dvr4geTkKArMFDVgy6jAvDYKq7KQWIhCWKBaVoyeCLPuefzJE7HW8MKWdS1a2VICzFY1J5VtidG20NKULVSsIlUNtdSEtCWndSs1MUBLbhK64M2o469WKCaBHsc+XWe2TrBfXY+/C35hZBN89Np3STDXwdPbEGH/O9VWfn/wg4sJv52l/TbAve/T3/n+Z87m4qWPlxikO8im05M1qbRmLabGUMw1lbitoMVVVZd1KWxZ6ETUrdcvELJkMOX6SZOzpx6esmXQOxdBxzGkyf9MoE5yfj6fzIWP/Oun7coHWB4Oizn6xzGRdGp2NsCHd+XbkNJ3fohXkGdoT9DZ82JXdjJwPD1VLgWFMTUYQTHjtMRcSoprWVa4repWtqKGUl/smj2CcQF0nPptw5imzcz+zpfLEJ1VOg7peL54Pdlp9uec8uuSV7L9rD4Nv5JuttLciZKkxNPp4Zwc9L/ezff39/f/AAAA//8DAKqnhx8oBgAA \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205214_5edd1c15b7e04c5a9b8e149237d1177f.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205214_5edd1c15b7e04c5a9b8e149237d1177f.trn new file mode 100644 index 0000000..afdbc0d --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205214_5edd1c15b7e04c5a9b8e149237d1177f.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACpSTQW/cIBCF/4rF2TiAMTa+RZtWXaVtVk3UVr1hZtxFdWCFcZoo2v9e4d0qSW+9WWPe8OZ7wzPx5h5JTy4Ph3cP6NNMSpLcWhJMKMoUZfWdYH0jei6rrulq2ckfpCTuGp9IT1ppLbcaaY0KqQTW0KEeLQVAzRkHJhqZe5qfM+mfiXEV4IOzWIX5K8bZBU968s15CL/z3cZVdgoLVDFMuPVzMt5mM5+3m2v68XJ3d7M7nZpxzuLKAekJswh8AEXH2nIqNRuo1h1QkMy0rW40ivYkcz5h9GaqZvj1cj+E5DHZXlSirhgVmhxLAiaZbHkwM949HbKLFdFVrpdref3sn8kDRtKL8i/MU7sLO7kLG+7vjYeL0Xk370lJDjEcMCaHK451wmkyyQVfnC/ZTA59IiW5uS12k0ljiPdvGN2eRt/+x+SfjN07j8X2Kms4Cqg7pKPgisoODDVNralStRqRW90KTkqyCT45v4RlLrY+4c+4uiQ9eW+mGU/+LqPdu4Q2LTFb/65y1vjo0iZALjBSkl0MsNhUvODmrGJVzfLPmyUdllR8QXARbcI8011czu3/kQglVtFmiRF9KnYm7YsPZt6TnmjTWW600cYoa0dsGI4MmFGsBVSgEKSAFqSWoKRmVkkzcAmm4cOYK9l3TrdYF0qhaZtWCjrYQVNZjxnRwChvOrRdbaHVedIXrMXNlHXGdkwi1FTjCFRqbqiWdU2hbkwr1Ni2YnyF5Jw4Z5k34MPpaZwcAHQIilMheUOl1px2umnp0HaDHlSHjc2erzF6nF6B+uRsDHMYU3FemOINui+Lz8+7WJfnt/P0cY3sKthfGIscuXE+b/M55ePxePwDAAD//wMAoa2QhCUEAAA= \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205214_68c8a986617347b9a8b8a6ccc867856d.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205214_68c8a986617347b9a8b8a6ccc867856d.trn new file mode 100644 index 0000000..1d92e0a --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205214_68c8a986617347b9a8b8a6ccc867856d.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACoxU227jNhD9FYGvFRXeRIl6S50saqRJjLXRFn0bkaOYjUwZFJXsIsi/F5KTzWVRoC+CMCTnnDlzZp5IgAOShpwfj5cPGNJIcpL8EhJMaMo0ZXInWFOKhquiLquKCf43yYm/wu+kIZWylluDVKJGqhwraSs7S51Dwxl3TJRqzgl3I2meCPjC4YO3WAzjHxhHPwTSkD99cMPjjA2+sP0wuSIOPa7DmCDYmczNenVFfz/f7G43p1sjjvPjwjvSEGbR8dZp2knLqTKspcbUjjrFoKpMaVBUp2c+JIwB+mJ092/4bkgBk21EIWTBqDDkOScOEsyUWxhx9/04s1gkupjj+RJefpsn8oCRNCJ/FfOU7sz2/uwwtpPv3dnyJTk5xuGIMXlc1LjdZm8kOCtYIbRgjOTkGuzeB8zWF9ltP5cItmYKnaQGO0eV4UCNkpI6WUIldFdVoiM5ucIYsH+X9drbOIxDl7IXkbMPONvz1WWAtscZ4wv0I5KcrMdtguCgHwJefkM7pVOuXZzm49UQkg/TMI3ZOiS8i/By/vp+NcWIIWUbSPvsNxj3pCEGasvBgAHQ1nZYMuyYY6BZ5VA7jU4JVzlllNPKMKsVtFw5KHnbzRGSk19nDbeTtTiOb2wWk/T9wiF76dOq9xjSBxlnl3AUTtZIO8E1VbUDCqU0VGupO+TWVIK/wuwg3mEiDenqtm2Zta1QhlsQBqTrmHS6M0JUFahOlpUSWpesAuuqSnDlZIUWZCdZLUhONnFwk02fWy2XBmzi8A/aNEtFGqJZWdYW2lZoxWswNSquVa0NVkZ1wJS0LfCyhrblDEpmTS0scG1Ata7sfqh0Ge58wAs/Hnv4/g63LnQhf7EANe9Afbr+JcIBH4d4f3OycXFzuSM5+TqFeSNk69kijz7Qb3qe6OupT363jwgO3fXg8Gcf4Twu2WlCEZC5rqNStZyq1nbUlK2ilTOVlqhQLmwuBnuPMZsdBj7MU/Uj2e02O4927xPaNMWZ318Lj9tttukhdUM8fNgk7wr7LEAhJGdzZxw+nLbRwlE7V6PTnArFS6qM4bQ2ZUXbqm5Nq2ss7czxdkrHKWVf0fmINi0Vv3hxwVzt0d7/LMarC14sytnste1piy3S/q8l9pyTA8I4RTws27p5IusQMC7IF9NpFNfh2ve9H9EOwY2k4axkBTOleGH4n/c454ViSj0/Pz//CwAA//8DADl0x2IbBgAA \ No newline at end of file diff --git a/.appdata/.dotnet/TelemetryStorageService/20260603205214_c95f34493fd742c0b0205d145ee9ca93.trn b/.appdata/.dotnet/TelemetryStorageService/20260603205214_c95f34493fd742c0b0205d145ee9ca93.trn new file mode 100644 index 0000000..d8364e4 --- /dev/null +++ b/.appdata/.dotnet/TelemetryStorageService/20260603205214_c95f34493fd742c0b0205d145ee9ca93.trn @@ -0,0 +1,5 @@ +https://dc.services.visualstudio.com/v2/track +Content-Type:application/x-json-stream +Content-Encoding:gzip + +H4sIAAAAAAAACpRTTW/cNhD9K8KcRZmkKErUzVin6MJNvEgWadEbxRnFhGVqQVFOAmP/e0DtBol764UgBnwz72P4CsE+E/Rwezq9e6GQFigh+a0kudSMa8bro+R9I3uhqk6ZlvPmXyjB39N36KFVzglniNWkiSnkDRvq0TFEMoIL5LJRuaf9skD/CtZXSC/eUTUvnykufg7Qw98+4Pw1z7a+ctO8YhXnifZhSTa4TObDfnfP/ro9HB8Ol1cLLRlceYQeuCMUA2o21k4wZfjAjOmQoeK2bU1jSLYXmA+JYrBTteDTr/k4p0DJ9bKSdcWZNHAuAW2ymfJgFzp+P2UWm0V3uV5u5e3av8ILRehl+dPMS7sbN/mb52VY/YQ3lzPZ5SmrPMX5RDF52jzZZE6TTX4OxXXSbvIUEpTw8Kk4TDaNc3x+Y9Sni/79/5B/zNPffSO3JsLdvIYEPYi6gxLeW/foAxX7u9xOkMS6IzZKoZnq0DLb1IZpXeuRhDOtFFDCbg7Jh3Vel2IfEn2JmwDo4Q87LXShfhvdo0/k0hqzqn903oVDnHF1qfjlv+AVr2rOM2hNpzUVHwl9JJco6zvG9drvPxCp5QbarTFSSMXBpsfiT7s8Qg/Gdk5YY4212rmRGk4jR241b5E0akIlsUVlFGpluNPKDkKhbcQw5gqUQDnuYtswzQV2TS2YJeyYItMwI7uRdQpHQW1jsHFvfCwepoyzruOKsGaGRmTKCMuMqmuGdWNbqce2leNvllzTFzwbjPRy+SsXBogdoRZMKtEwZYxgnWlaNrTdYAbdUeMy53uKgabfjHrvXZyXeUzFdXmKN9Z9XEP+78W2SF99YN+2jO5m90SxyBlbH/J6X2M9n8/nHwAAAP//AwCAbbUbNgQAAA== \ No newline at end of file diff --git a/.appdata/buildverify/PlotLine.dll b/.appdata/buildverify/PlotLine.dll index 663c582..a1e85e3 100644 Binary files a/.appdata/buildverify/PlotLine.dll and b/.appdata/buildverify/PlotLine.dll differ diff --git a/PlotLine/Controllers/CharactersController.cs b/PlotLine/Controllers/CharactersController.cs index 56a40e1..43c0ac7 100644 --- a/PlotLine/Controllers/CharactersController.cs +++ b/PlotLine/Controllers/CharactersController.cs @@ -52,6 +52,15 @@ public sealed class CharactersController(ICharacterService characters) : Control return RedirectToAction(nameof(Index), new { projectId }); } + [HttpPost] + [ValidateAntiForgeryToken] + public async Task SaveInitialRelationship(InitialRelationshipEditViewModel model) + { + await characters.SaveInitialRelationshipAsync(model); + TempData["RelationshipMessage"] = model.CharacterRelationshipID == 0 ? "Initial relationship added." : "Initial relationship updated."; + return RedirectToAction(nameof(Details), new { id = model.ReturnCharacterID ?? model.CharacterID }); + } + [HttpPost] [ValidateAntiForgeryToken] public async Task ArchiveRelationship(int id, int characterId) diff --git a/PlotLine/Data/Repositories.cs b/PlotLine/Data/Repositories.cs index 31c7cd3..19b51ea 100644 --- a/PlotLine/Data/Repositories.cs +++ b/PlotLine/Data/Repositories.cs @@ -143,6 +143,7 @@ public interface ICharacterRepository Task DeleteKnowledgeAsync(int characterKnowledgeId); Task> ListRelationshipsByProjectAsync(int projectId); Task> ListRelationshipsByCharacterAsync(int characterId); + Task> ListInitialRelationshipsByCharacterAsync(int characterId); Task SaveRelationshipAsync(CharacterRelationship relationship); Task ArchiveRelationshipAsync(int characterRelationshipId); Task> ListRelationshipEventsBySceneAsync(int sceneId); @@ -2132,11 +2133,24 @@ public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory) relationship.StartSceneID, relationship.EndSceneID, relationship.IsKnownToReader, - relationship.Notes + relationship.Notes, + relationship.IsInitialRelationship, + relationship.InitialBookID, + relationship.ReaderInitiallyKnows, + relationship.InitialRelationshipStateID, + relationship.InitialIntensity, + relationship.IsReciprocal }, commandType: CommandType.StoredProcedure); } + public async Task> ListInitialRelationshipsByCharacterAsync(int characterId) + { + using var connection = connectionFactory.CreateConnection(); + var rows = await connection.QueryAsync("dbo.Relationship_ListInitialByCharacter", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure); + return rows.ToList(); + } + public async Task ArchiveRelationshipAsync(int characterRelationshipId) { using var connection = connectionFactory.CreateConnection(); diff --git a/PlotLine/Models/CoreModels.cs b/PlotLine/Models/CoreModels.cs index 67da506..4b75f62 100644 --- a/PlotLine/Models/CoreModels.cs +++ b/PlotLine/Models/CoreModels.cs @@ -651,6 +651,14 @@ public sealed class CharacterRelationship public int? EndSceneID { get; set; } public bool IsKnownToReader { get; set; } public string? Notes { get; set; } + public bool IsInitialRelationship { get; set; } + public int? InitialBookID { get; set; } + public string? InitialBookTitle { get; set; } + public bool ReaderInitiallyKnows { get; set; } + public int? InitialRelationshipStateID { get; set; } + public string? InitialRelationshipStateName { get; set; } + public int? InitialIntensity { get; set; } + public bool IsReciprocal { get; set; } public DateTime CreatedDate { get; set; } public DateTime UpdatedDate { get; set; } public bool IsArchived { get; set; } @@ -666,6 +674,7 @@ public sealed class RelationshipEvent public int CharacterBID { get; set; } public string CharacterBName { get; set; } = string.Empty; public string RelationshipTypeName { get; set; } = string.Empty; + public bool IsReciprocal { get; set; } public int SceneID { get; set; } public decimal SceneNumber { get; set; } public string SceneTitle { get; set; } = string.Empty; @@ -1121,7 +1130,11 @@ public sealed class StoryBibleRelationshipSummary public int CharacterBID { get; set; } public string CharacterBName { get; set; } = string.Empty; public string RelationshipTypeName { get; set; } = string.Empty; + public string? InitialRelationshipName { get; set; } + public string? InitialStateName { get; set; } + public int? InitialIntensity { get; set; } public string? CurrentStateName { get; set; } + public int? CurrentIntensity { get; set; } public int EventCount { get; set; } public string? FirstEventLabel { get; set; } public string? EventHistory { get; set; } diff --git a/PlotLine/Services/CoreServices.cs b/PlotLine/Services/CoreServices.cs index 5dd226b..2048d3e 100644 --- a/PlotLine/Services/CoreServices.cs +++ b/PlotLine/Services/CoreServices.cs @@ -124,6 +124,7 @@ public interface ICharacterService Task SaveCharacterAsync(CharacterEditViewModel model); Task ArchiveCharacterAsync(int characterId); Task ArchiveRelationshipAsync(int characterRelationshipId); + Task SaveInitialRelationshipAsync(InitialRelationshipEditViewModel model); Task AddSceneCharacterAsync(SceneCharacterCreateViewModel model); Task UpdateSceneCharacterAsync(SceneCharacterEditViewModel model); Task DeleteSceneCharacterAsync(int sceneCharacterId); @@ -2438,7 +2439,15 @@ public sealed class ExportService(IExportRepository exports) : IExportService { if (!rows.Any()) return; builder.AppendLine("## Relationships").AppendLine(); - foreach (var r in rows) builder.AppendLine($"- {r.CharacterAName} / {r.CharacterBName}: {r.RelationshipTypeName}; {r.CurrentStateName ?? "No state"}"); + foreach (var r in rows) + { + var initial = r.InitialRelationshipName ?? r.RelationshipTypeName; + if (!string.IsNullOrWhiteSpace(r.InitialStateName)) initial += $" / {r.InitialStateName}"; + if (r.InitialIntensity.HasValue) initial += $" (intensity {r.InitialIntensity})"; + var current = r.CurrentStateName ?? "No current state"; + if (r.CurrentIntensity.HasValue) current += $" (intensity {r.CurrentIntensity})"; + builder.AppendLine($"- {r.CharacterAName} / {r.CharacterBName}: Initial: {initial}; Current: {current}; Events: {r.EventHistory ?? "No event history"}"); + } builder.AppendLine(); } @@ -3112,7 +3121,7 @@ public sealed class LocationService(IProjectRepository projects, ILocationReposi } } -public sealed class CharacterService(IProjectRepository projects, ICharacterRepository characters) : ICharacterService +public sealed class CharacterService(IProjectRepository projects, IBookRepository books, ICharacterRepository characters) : ICharacterService { public async Task GetCharactersAsync(int projectId) { @@ -3170,6 +3179,17 @@ public sealed class CharacterService(IProjectRepository projects, ICharacterRepo return null; } + var projectCharacters = await characters.ListCharactersAsync(project.ProjectID); + var relationshipLookups = await characters.GetLookupsAsync(); + var projectBooks = await books.ListByProjectAsync(project.ProjectID); + + var relationshipContainers = await characters.ListRelationshipsByCharacterAsync(character.CharacterID); + var initialRelationships = await characters.ListInitialRelationshipsByCharacterAsync(character.CharacterID); + var relationshipEvents = await characters.ListRelationshipEventsByCharacterAsync(character.CharacterID); + var eventsByRelationship = relationshipEvents + .GroupBy(x => x.CharacterRelationshipID) + .ToDictionary(x => x.Key, x => (IReadOnlyList)x.ToList()); + return new CharacterDetailViewModel { Project = project, @@ -3178,8 +3198,37 @@ public sealed class CharacterService(IProjectRepository projects, ICharacterRepo Appearances = await GetAppearancesByCharacterAsync(character.CharacterID), AttributeEvents = await characters.ListAttributeEventsByCharacterAsync(character.CharacterID), KnowledgeItems = await characters.ListKnowledgeByCharacterAsync(character.CharacterID), - Relationships = await characters.ListRelationshipsByCharacterAsync(character.CharacterID), - RelationshipEvents = await characters.ListRelationshipEventsByCharacterAsync(character.CharacterID) + InitialRelationships = initialRelationships, + CurrentRelationships = relationshipContainers + .Select(relationship => + { + eventsByRelationship.TryGetValue(relationship.CharacterRelationshipID, out var events); + events ??= []; + return new CurrentRelationshipViewModel + { + Relationship = relationship, + LatestEvent = events.LastOrDefault(), + Events = events + }; + }) + .OrderBy(x => x.Relationship.CharacterAName) + .ThenBy(x => x.Relationship.CharacterBName) + .ThenBy(x => x.Relationship.RelationshipTypeName) + .ToList(), + RelationshipEvents = relationshipEvents, + NewInitialRelationship = new InitialRelationshipEditViewModel + { + ProjectID = project.ProjectID, + CharacterID = character.CharacterID, + ReturnCharacterID = character.CharacterID, + InitialIntensity = 5, + ReaderInitiallyKnows = true, + IsReciprocal = true + }, + CharacterOptions = projectCharacters.Select(x => new SelectListItem(x.CharacterName, x.CharacterID.ToString())).ToList(), + RelationshipTypeOptions = ChapterService.ToSelectList(relationshipLookups.RelationshipTypes, x => x.RelationshipTypeID, x => x.TypeName), + RelationshipStateOptions = relationshipLookups.RelationshipStates.Select(x => new SelectListItem(x.StateName, x.RelationshipStateID.ToString())).ToList(), + BookOptions = projectBooks.Select(x => new SelectListItem($"Book {x.BookNumber}: {x.BookTitle}", x.BookID.ToString())).ToList() }; } @@ -3203,6 +3252,37 @@ public sealed class CharacterService(IProjectRepository projects, ICharacterRepo public Task ArchiveRelationshipAsync(int characterRelationshipId) => characters.ArchiveRelationshipAsync(characterRelationshipId); + public async Task SaveInitialRelationshipAsync(InitialRelationshipEditViewModel model) + { + if (model.RelatedCharacterID == 0) + { + throw new InvalidOperationException("Choose a related character."); + } + + if (model.RelationshipTypeID == 0) + { + throw new InvalidOperationException("Choose a relationship type."); + } + + return await characters.SaveRelationshipAsync(new CharacterRelationship + { + CharacterRelationshipID = model.CharacterRelationshipID, + ProjectID = model.ProjectID, + CharacterAID = model.CharacterID, + CharacterBID = model.RelatedCharacterID, + RelationshipTypeID = model.RelationshipTypeID, + IsPermanent = true, + IsKnownToReader = model.ReaderInitiallyKnows, + Notes = model.Notes, + IsInitialRelationship = true, + InitialBookID = model.InitialBookID, + ReaderInitiallyKnows = model.ReaderInitiallyKnows, + InitialRelationshipStateID = model.InitialRelationshipStateID, + InitialIntensity = model.InitialIntensity, + IsReciprocal = model.IsReciprocal + }); + } + public async Task AddSceneCharacterAsync(SceneCharacterCreateViewModel model) { var characterId = model.CharacterID.GetValueOrDefault(); diff --git a/PlotLine/Sql/030_Phase2A_RelationshipFoundations.sql b/PlotLine/Sql/030_Phase2A_RelationshipFoundations.sql new file mode 100644 index 0000000..a627158 --- /dev/null +++ b/PlotLine/Sql/030_Phase2A_RelationshipFoundations.sql @@ -0,0 +1,248 @@ +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'IsInitialRelationship') IS NULL +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD IsInitialRelationship bit NOT NULL CONSTRAINT DF_CharacterRelationships_IsInitialRelationship DEFAULT 0; +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'InitialBookID') IS NULL +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD InitialBookID int NULL; +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'InitialBookID') IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_CharacterRelationships_InitialBook') +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD CONSTRAINT FK_CharacterRelationships_InitialBook FOREIGN KEY (InitialBookID) REFERENCES dbo.Books(BookID); +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'ReaderInitiallyKnows') IS NULL +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD ReaderInitiallyKnows bit NOT NULL CONSTRAINT DF_CharacterRelationships_ReaderInitiallyKnows DEFAULT 0; +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'InitialRelationshipStateID') IS NULL +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD InitialRelationshipStateID int NULL; +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'InitialRelationshipStateID') IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_CharacterRelationships_InitialState') +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD CONSTRAINT FK_CharacterRelationships_InitialState FOREIGN KEY (InitialRelationshipStateID) REFERENCES dbo.RelationshipStates(RelationshipStateID); +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'InitialIntensity') IS NULL +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD InitialIntensity int NULL; +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'InitialIntensity') IS NOT NULL + AND NOT EXISTS (SELECT 1 FROM sys.check_constraints WHERE name = N'CK_CharacterRelationships_InitialIntensity') +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD CONSTRAINT CK_CharacterRelationships_InitialIntensity CHECK (InitialIntensity IS NULL OR InitialIntensity BETWEEN 1 AND 10); +END; +GO + +IF COL_LENGTH(N'dbo.CharacterRelationships', N'IsReciprocal') IS NULL +BEGIN + ALTER TABLE dbo.CharacterRelationships + ADD IsReciprocal bit NOT NULL CONSTRAINT DF_CharacterRelationships_IsReciprocal DEFAULT 0; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.Relationship_ListInitialByCharacter + @CharacterID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, + cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, + cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, + cr.IsInitialRelationship, cr.InitialBookID, b.BookTitle AS InitialBookTitle, cr.ReaderInitiallyKnows, + cr.InitialRelationshipStateID, rs.StateName AS InitialRelationshipStateName, cr.InitialIntensity, cr.IsReciprocal, + cr.CreatedDate, cr.UpdatedDate, cr.IsArchived + FROM dbo.CharacterRelationships cr + INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID + INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID + INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID + LEFT JOIN dbo.Books b ON b.BookID = cr.InitialBookID + LEFT JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = cr.InitialRelationshipStateID + WHERE cr.IsArchived = 0 + AND cr.IsInitialRelationship = 1 + AND @CharacterID IN (cr.CharacterAID, cr.CharacterBID) + ORDER BY COALESCE(b.SortOrder, 0), ca.CharacterName, cb.CharacterName, rt.SortOrder; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.Relationship_ListByCharacter + @CharacterID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, + cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, + cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, + cr.IsInitialRelationship, cr.InitialBookID, b.BookTitle AS InitialBookTitle, cr.ReaderInitiallyKnows, + cr.InitialRelationshipStateID, rs.StateName AS InitialRelationshipStateName, cr.InitialIntensity, cr.IsReciprocal, + cr.CreatedDate, cr.UpdatedDate, cr.IsArchived + FROM dbo.CharacterRelationships cr + INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID + INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID + INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID + LEFT JOIN dbo.Books b ON b.BookID = cr.InitialBookID + LEFT JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = cr.InitialRelationshipStateID + WHERE cr.IsArchived = 0 AND @CharacterID IN (cr.CharacterAID, cr.CharacterBID) + ORDER BY ca.CharacterName, cb.CharacterName, rt.SortOrder; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.Relationship_ListByProject + @ProjectID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT cr.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, + cr.CharacterBID, cb.CharacterName AS CharacterBName, cr.RelationshipTypeID, rt.TypeName AS RelationshipTypeName, + cr.IsPermanent, cr.StartSceneID, cr.EndSceneID, cr.IsKnownToReader, cr.Notes, + cr.IsInitialRelationship, cr.InitialBookID, b.BookTitle AS InitialBookTitle, cr.ReaderInitiallyKnows, + cr.InitialRelationshipStateID, rs.StateName AS InitialRelationshipStateName, cr.InitialIntensity, cr.IsReciprocal, + cr.CreatedDate, cr.UpdatedDate, cr.IsArchived + FROM dbo.CharacterRelationships cr + INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID + INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID + INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID + LEFT JOIN dbo.Books b ON b.BookID = cr.InitialBookID + LEFT JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = cr.InitialRelationshipStateID + WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0 + ORDER BY ca.CharacterName, cb.CharacterName, rt.SortOrder; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.Relationship_Save + @CharacterRelationshipID int = NULL, + @ProjectID int, + @CharacterAID int, + @CharacterBID int, + @RelationshipTypeID int, + @IsPermanent bit, + @StartSceneID int = NULL, + @EndSceneID int = NULL, + @IsKnownToReader bit, + @Notes nvarchar(max) = NULL, + @IsInitialRelationship bit = 0, + @InitialBookID int = NULL, + @ReaderInitiallyKnows bit = 0, + @InitialRelationshipStateID int = NULL, + @InitialIntensity int = NULL, + @IsReciprocal bit = 0 +AS +BEGIN + SET NOCOUNT ON; + + IF @InitialIntensity IS NOT NULL AND (@InitialIntensity < 1 OR @InitialIntensity > 10) + THROW 53000, 'Initial relationship intensity must be between 1 and 10.', 1; + + IF @CharacterRelationshipID IS NULL OR @CharacterRelationshipID = 0 + BEGIN + INSERT dbo.CharacterRelationships + (ProjectID, CharacterAID, CharacterBID, RelationshipTypeID, IsPermanent, StartSceneID, EndSceneID, IsKnownToReader, Notes, + IsInitialRelationship, InitialBookID, ReaderInitiallyKnows, InitialRelationshipStateID, InitialIntensity, IsReciprocal) + VALUES + (@ProjectID, @CharacterAID, @CharacterBID, @RelationshipTypeID, @IsPermanent, @StartSceneID, @EndSceneID, @IsKnownToReader, @Notes, + @IsInitialRelationship, @InitialBookID, @ReaderInitiallyKnows, @InitialRelationshipStateID, @InitialIntensity, @IsReciprocal); + + SET @CharacterRelationshipID = CAST(SCOPE_IDENTITY() AS int); + END + ELSE + BEGIN + UPDATE dbo.CharacterRelationships + SET CharacterAID = @CharacterAID, + CharacterBID = @CharacterBID, + RelationshipTypeID = @RelationshipTypeID, + IsPermanent = @IsPermanent, + StartSceneID = @StartSceneID, + EndSceneID = @EndSceneID, + IsKnownToReader = @IsKnownToReader, + Notes = @Notes, + IsInitialRelationship = @IsInitialRelationship, + InitialBookID = @InitialBookID, + ReaderInitiallyKnows = @ReaderInitiallyKnows, + InitialRelationshipStateID = @InitialRelationshipStateID, + InitialIntensity = @InitialIntensity, + IsReciprocal = @IsReciprocal, + UpdatedDate = SYSUTCDATETIME() + WHERE CharacterRelationshipID = @CharacterRelationshipID + AND ProjectID = @ProjectID; + END + + SELECT @CharacterRelationshipID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.RelationshipEvent_ListByCharacter + @CharacterID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT re.RelationshipEventID, re.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, + cr.CharacterBID, cb.CharacterName AS CharacterBName, rt.TypeName AS RelationshipTypeName, + cr.IsReciprocal, re.SceneID, s.SceneNumber, s.SceneTitle, ch.ChapterNumber, b.BookTitle, + re.RelationshipStateID, rs.StateName AS RelationshipStateName, re.Intensity, re.IsKnownToOtherCharacter, + re.Description, re.CreatedDate, re.UpdatedDate + FROM dbo.RelationshipEvents re + INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID + INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID + INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID + INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID + INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID + INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID + INNER JOIN dbo.Chapters ch ON ch.ChapterID = s.ChapterID + INNER JOIN dbo.Books b ON b.BookID = ch.BookID + WHERE @CharacterID IN (cr.CharacterAID, cr.CharacterBID) + ORDER BY b.SortOrder, ch.SortOrder, s.SortOrder, re.CreatedDate, re.RelationshipEventID; +END; +GO + +CREATE OR ALTER PROCEDURE dbo.RelationshipEvent_ListByScene + @SceneID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT re.RelationshipEventID, re.CharacterRelationshipID, cr.ProjectID, cr.CharacterAID, ca.CharacterName AS CharacterAName, + cr.CharacterBID, cb.CharacterName AS CharacterBName, rt.TypeName AS RelationshipTypeName, + cr.IsReciprocal, re.SceneID, re.RelationshipStateID, rs.StateName AS RelationshipStateName, re.Intensity, re.IsKnownToOtherCharacter, + re.Description, re.CreatedDate, re.UpdatedDate + FROM dbo.RelationshipEvents re + INNER JOIN dbo.CharacterRelationships cr ON cr.CharacterRelationshipID = re.CharacterRelationshipID + INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID + INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID + INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID + INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID + WHERE re.SceneID = @SceneID + ORDER BY ca.CharacterName, cb.CharacterName, re.CreatedDate, re.RelationshipEventID; +END; +GO diff --git a/PlotLine/Sql/031_Phase2B_RelationshipTimeline.sql b/PlotLine/Sql/031_Phase2B_RelationshipTimeline.sql new file mode 100644 index 0000000..8a9e515 --- /dev/null +++ b/PlotLine/Sql/031_Phase2B_RelationshipTimeline.sql @@ -0,0 +1,69 @@ +SET ANSI_NULLS ON; +GO +SET QUOTED_IDENTIFIER ON; +GO + +CREATE OR ALTER PROCEDURE dbo.StoryBible_RelationshipSummaries + @ProjectID int +AS +BEGIN + SET NOCOUNT ON; + + SELECT cr.CharacterRelationshipID, + cr.CharacterAID, + ca.CharacterName AS CharacterAName, + cr.CharacterBID, + cb.CharacterName AS CharacterBName, + rt.TypeName AS RelationshipTypeName, + CASE WHEN cr.IsInitialRelationship = 1 THEN rt.TypeName ELSE NULL END AS InitialRelationshipName, + initialState.StateName AS InitialStateName, + cr.InitialIntensity, + COALESCE(currentEvent.StateName, initialState.StateName, rt.TypeName) AS CurrentStateName, + COALESCE(currentEvent.Intensity, cr.InitialIntensity) AS CurrentIntensity, + ISNULL(events.EventCount, 0) AS EventCount, + firstEvent.SceneLabel AS FirstEventLabel, + events.EventHistory + FROM dbo.CharacterRelationships cr + INNER JOIN dbo.Characters ca ON ca.CharacterID = cr.CharacterAID + INNER JOIN dbo.Characters cb ON cb.CharacterID = cr.CharacterBID + INNER JOIN dbo.RelationshipTypes rt ON rt.RelationshipTypeID = cr.RelationshipTypeID + LEFT JOIN dbo.RelationshipStates initialState ON initialState.RelationshipStateID = cr.InitialRelationshipStateID + OUTER APPLY + ( + SELECT TOP (1) rs.StateName, re.Intensity + FROM dbo.RelationshipEvents re + INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID + INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID AND s.IsArchived = 0 + INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0 + INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0 + WHERE re.CharacterRelationshipID = cr.CharacterRelationshipID + ORDER BY b.SortOrder DESC, c.SortOrder DESC, s.SortOrder DESC, re.CreatedDate DESC, re.RelationshipEventID DESC + ) currentEvent + OUTER APPLY + ( + SELECT COUNT(*) AS EventCount, + STRING_AGG( + CONVERT(nvarchar(max), CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', rs.StateName, ISNULL(N' (' + CONVERT(nvarchar(20), re.Intensity) + N')', N''), ISNULL(N' - ' + re.Description, N''))), + N' -> ' + ) WITHIN GROUP (ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, re.CreatedDate, re.RelationshipEventID) AS EventHistory + FROM dbo.RelationshipEvents re + INNER JOIN dbo.RelationshipStates rs ON rs.RelationshipStateID = re.RelationshipStateID + INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID AND s.IsArchived = 0 + INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0 + INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0 + WHERE re.CharacterRelationshipID = cr.CharacterRelationshipID + ) events + OUTER APPLY + ( + SELECT TOP (1) CONCAT(b.BookTitle, N' / Ch ', c.ChapterNumber, N' / Scene ', s.SceneNumber, N': ', s.SceneTitle) AS SceneLabel + FROM dbo.RelationshipEvents re + INNER JOIN dbo.Scenes s ON s.SceneID = re.SceneID AND s.IsArchived = 0 + INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID AND c.IsArchived = 0 + INNER JOIN dbo.Books b ON b.BookID = c.BookID AND b.IsArchived = 0 + WHERE re.CharacterRelationshipID = cr.CharacterRelationshipID + ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, re.CreatedDate, re.RelationshipEventID + ) firstEvent + WHERE cr.ProjectID = @ProjectID AND cr.IsArchived = 0 + ORDER BY ca.CharacterName, cb.CharacterName; +END; +GO diff --git a/PlotLine/ViewModels/CoreViewModels.cs b/PlotLine/ViewModels/CoreViewModels.cs index 8698009..7dcb9d9 100644 --- a/PlotLine/ViewModels/CoreViewModels.cs +++ b/PlotLine/ViewModels/CoreViewModels.cs @@ -938,8 +938,42 @@ public sealed class CharacterDetailViewModel public IReadOnlyList Appearances { get; set; } = []; public IReadOnlyList AttributeEvents { get; set; } = []; public IReadOnlyList KnowledgeItems { get; set; } = []; - public IReadOnlyList Relationships { get; set; } = []; + public IReadOnlyList InitialRelationships { get; set; } = []; + public IReadOnlyList CurrentRelationships { get; set; } = []; public IReadOnlyList RelationshipEvents { get; set; } = []; + public InitialRelationshipEditViewModel NewInitialRelationship { get; set; } = new(); + public IReadOnlyList CharacterOptions { get; set; } = []; + public IReadOnlyList RelationshipTypeOptions { get; set; } = []; + public IReadOnlyList RelationshipStateOptions { get; set; } = []; + public IReadOnlyList BookOptions { get; set; } = []; +} + +public sealed class CurrentRelationshipViewModel +{ + public CharacterRelationship Relationship { get; set; } = new(); + public RelationshipEvent? LatestEvent { get; set; } + public IReadOnlyList Events { get; set; } = []; + public string CurrentStateName => LatestEvent?.RelationshipStateName + ?? Relationship.InitialRelationshipStateName + ?? Relationship.RelationshipTypeName; + public int? CurrentIntensity => LatestEvent?.Intensity ?? Relationship.InitialIntensity; + public string? CurrentNotes => LatestEvent?.Description ?? Relationship.Notes; +} + +public sealed class InitialRelationshipEditViewModel +{ + public int CharacterRelationshipID { get; set; } + public int ProjectID { get; set; } + public int CharacterID { get; set; } + public int? ReturnCharacterID { get; set; } + public int RelatedCharacterID { get; set; } + public int RelationshipTypeID { get; set; } + public int? InitialBookID { get; set; } + public int? InitialRelationshipStateID { get; set; } + public int? InitialIntensity { get; set; } + public bool IsReciprocal { get; set; } + public bool ReaderInitiallyKnows { get; set; } + public string? Notes { get; set; } } public sealed class SceneCharacterCreateViewModel diff --git a/PlotLine/Views/Characters/Details.cshtml b/PlotLine/Views/Characters/Details.cshtml index c72eff9..72d41a9 100644 --- a/PlotLine/Views/Characters/Details.cshtml +++ b/PlotLine/Views/Characters/Details.cshtml @@ -53,6 +53,11 @@ +@if (TempData["RelationshipMessage"] is string relationshipMessage) +{ +
@relationshipMessage
+} +

Profile

@@ -64,32 +69,265 @@

Story importance: @storyImportance

-

Relationships

- @if (!Model.Relationships.Any()) +

Initial Relationships

+ @if (!Model.InitialRelationships.Any()) { -

No relationships yet.

+

No initial relationships yet.

} else {
- @foreach (var relationship in Model.Relationships) + @foreach (var relationship in Model.InitialRelationships) { + var initialSourceId = relationship.CharacterAID; + var initialRelatedId = relationship.CharacterBID;
@relationship.CharacterAName -> @relationship.CharacterBName @relationship.RelationshipTypeName + Reciprocal: @(relationship.IsReciprocal ? "Yes" : "No") + Reader initially knows: @(relationship.ReaderInitiallyKnows ? "Yes" : "No") + @if (!string.IsNullOrWhiteSpace(relationship.InitialRelationshipStateName) || relationship.InitialIntensity.HasValue) + { + @relationship.InitialRelationshipStateName@if (relationship.InitialIntensity.HasValue) { / Intensity @relationship.InitialIntensity } + } + @if (!string.IsNullOrWhiteSpace(relationship.InitialBookTitle)) + { + Initial book: @relationship.InitialBookTitle + } + @if (!string.IsNullOrWhiteSpace(relationship.Notes)) + { + @relationship.Notes + } + Open related character Evolution +
+ Edit +
+ + + + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
- +
}
} +
+ Add initial relationship +
+ + + +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+

Current Relationships

+ @if (!Model.CurrentRelationships.Any()) + { +

No current relationship state yet.

+ } + else + { +
+ @foreach (var current in Model.CurrentRelationships) + { + var relationship = current.Relationship; + var relatedCharacterId = relationship.CharacterAID == Model.Character.CharacterID ? relationship.CharacterBID : relationship.CharacterAID; + var relatedCharacterName = relationship.CharacterAID == Model.Character.CharacterID ? relationship.CharacterBName : relationship.CharacterAName; +
+ @relatedCharacterName + @relationship.RelationshipTypeName · Current: @current.CurrentStateName + Reciprocal: @(relationship.IsReciprocal ? "Yes" : "No") · Confidence / intensity: @(current.CurrentIntensity?.ToString() ?? "Unknown") + @if (!string.IsNullOrWhiteSpace(current.CurrentNotes)) + { + @current.CurrentNotes + } + Open related character + Evolution +
+ View History +
+
+ Initial: @relationship.RelationshipTypeName + @(relationship.InitialRelationshipStateName ?? "No awareness state")@if (relationship.InitialIntensity.HasValue) { · Intensity @relationship.InitialIntensity } + Reader initially knows: @(relationship.ReaderInitiallyKnows ? "Yes" : "No") + @if (!string.IsNullOrWhiteSpace(relationship.Notes)) + { + @relationship.Notes + } +
+ @foreach (var item in current.Events) + { +
+ @item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle + @item.RelationshipStateName · Intensity @(item.Intensity?.ToString() ?? "Unknown") + Awareness: @(item.IsKnownToOtherCharacter ? "known to other character" : "not known to other character") + @if (!string.IsNullOrWhiteSpace(item.Description)) + { + @item.Description + } + Open scene +
+ } +
+ Current: @current.CurrentStateName + Confidence / intensity: @(current.CurrentIntensity?.ToString() ?? "Unknown") +
+
+
+
+ } +
+ } +
+ +
+

Relationship Events

+ @if (!Model.RelationshipEvents.Any()) + { +

No relationship events yet.

+ } + else + { +
+ @foreach (var item in Model.RelationshipEvents) + { +
+ @item.CharacterAName -> @item.CharacterBName: @item.RelationshipStateName + @item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle + @item.RelationshipTypeName · Awareness: @(item.IsKnownToOtherCharacter ? "known to other character" : "not known to other character") + Reciprocal: @(item.IsReciprocal ? "Yes" : "No") · Confidence / intensity: @(item.Intensity?.ToString() ?? "Unknown") + @if (!string.IsNullOrWhiteSpace(item.Description)) + { + @item.Description + } + Open in timeline + Open related character +
+ } +
+ } +
+

Scene appearances

@if (!Model.Appearances.Any()) @@ -156,24 +394,3 @@ }
-
-

Relationship events

- @if (!Model.RelationshipEvents.Any()) - { -

No relationship events yet.

- } - else - { -
- @foreach (var item in Model.RelationshipEvents) - { -
- @item.CharacterAName -> @item.CharacterBName: @item.RelationshipStateName - @item.BookTitle / Ch @item.ChapterNumber / Scene @item.SceneNumber: @item.SceneTitle - Intensity @item.Intensity. @item.Description - Open in timeline -
- } -
- } -
diff --git a/PlotLine/Views/Scenes/_SceneInspector.cshtml b/PlotLine/Views/Scenes/_SceneInspector.cshtml index 8fbcaa7..7f8808a 100644 --- a/PlotLine/Views/Scenes/_SceneInspector.cshtml +++ b/PlotLine/Views/Scenes/_SceneInspector.cshtml @@ -1274,16 +1274,29 @@
-
- -
+ @if (Model.RelationshipOptions.Count == 1) + { +
+ + @Model.RelationshipOptions[0].Text +
+ } + else + { +
+ +
+ }
-
+
+ + +