@strands.gg/accui 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/accui.css CHANGED
@@ -48,6 +48,8 @@
48
48
  --color-blue-900: oklch(37.9% 0.146 265.522);
49
49
  --color-purple-50: oklch(97.7% 0.014 308.299);
50
50
  --color-purple-100: oklch(94.6% 0.033 307.174);
51
+ --color-purple-200: oklch(90.2% 0.063 306.703);
52
+ --color-purple-700: oklch(49.6% 0.265 301.924);
51
53
  --color-gray-50: oklch(98.5% 0.002 247.839);
52
54
  --color-gray-100: oklch(96.7% 0.003 264.542);
53
55
  --color-gray-200: oklch(92.8% 0.006 264.531);
@@ -410,6 +412,9 @@
410
412
  .mr-1 {
411
413
  margin-right: calc(var(--spacing) * 1);
412
414
  }
415
+ .mr-1\.5 {
416
+ margin-right: calc(var(--spacing) * 1.5);
417
+ }
413
418
  .mr-2 {
414
419
  margin-right: calc(var(--spacing) * 2);
415
420
  }
@@ -440,6 +445,9 @@
440
445
  .mb-12 {
441
446
  margin-bottom: calc(var(--spacing) * 12);
442
447
  }
448
+ .ml-1 {
449
+ margin-left: calc(var(--spacing) * 1);
450
+ }
443
451
  .ml-2 {
444
452
  margin-left: calc(var(--spacing) * 2);
445
453
  }
@@ -770,6 +778,9 @@
770
778
  .border-neutral-200 {
771
779
  border-color: var(--color-neutral-200);
772
780
  }
781
+ .border-purple-200 {
782
+ border-color: var(--color-purple-200);
783
+ }
773
784
  .border-red-200 {
774
785
  border-color: var(--color-red-200);
775
786
  }
@@ -1135,6 +1146,9 @@
1135
1146
  .text-neutral-900 {
1136
1147
  color: var(--color-neutral-900);
1137
1148
  }
1149
+ .text-purple-700 {
1150
+ color: var(--color-purple-700);
1151
+ }
1138
1152
  .text-red-400 {
1139
1153
  color: var(--color-red-400);
1140
1154
  }
@@ -9199,6 +9199,7 @@ const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
9199
9199
  const emit = __emit;
9200
9200
  const { getSupportEmail, config: strandsConfig, getUrl } = useStrandsConfig.useStrandsConfig(props.config);
9201
9201
  const { fetchProfile, updateProfile, changeEmail, currentUser: authUser, currentSession, isAuthenticated, refreshToken, signOut } = useStrandsAuth.useStrandsAuth();
9202
+ const { mfaDevices: mfaDevices2, mfaEnabled: mfaEnabled2, activeMfaDevices, fetchMfaDevices } = useStrandsMfa();
9202
9203
  const internalUser = vue.ref(null);
9203
9204
  const fetchingProfile = vue.ref(false);
9204
9205
  const currentUser = vue.computed(() => {
@@ -9276,6 +9277,54 @@ const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
9276
9277
  const years = Math.floor(diffDays / 365);
9277
9278
  return years === 1 ? "1 year ago" : `${years} years ago`;
9278
9279
  });
9280
+ const deviceTypeCounts = vue.computed(() => {
9281
+ const devices = activeMfaDevices.value || [];
9282
+ const counts = {
9283
+ totp: 0,
9284
+ email: 0,
9285
+ hardware: 0,
9286
+ passkey: 0
9287
+ };
9288
+ devices.forEach((device) => {
9289
+ if (device.device_type in counts) {
9290
+ counts[device.device_type]++;
9291
+ }
9292
+ });
9293
+ return counts;
9294
+ });
9295
+ const mfaDeviceChips = vue.computed(() => {
9296
+ const chips = [];
9297
+ const counts = deviceTypeCounts.value;
9298
+ if (counts.totp > 0) {
9299
+ chips.push({
9300
+ type: "totp",
9301
+ count: counts.totp,
9302
+ label: counts.totp === 1 ? "Auth app" : "Auth apps",
9303
+ icon: Smartphone,
9304
+ color: "bg-blue-50 text-blue-700 border-blue-200"
9305
+ });
9306
+ }
9307
+ if (counts.email > 0) {
9308
+ chips.push({
9309
+ type: "email",
9310
+ count: counts.email,
9311
+ label: "Email",
9312
+ icon: Mail,
9313
+ color: "bg-green-50 text-green-700 border-green-200"
9314
+ });
9315
+ }
9316
+ if (counts.hardware > 0 || counts.passkey > 0) {
9317
+ const totalHardware = counts.hardware + counts.passkey;
9318
+ chips.push({
9319
+ type: "hardware",
9320
+ count: totalHardware,
9321
+ label: totalHardware === 1 ? "Key" : "Keys",
9322
+ icon: KeyRound,
9323
+ color: "bg-purple-50 text-purple-700 border-purple-200"
9324
+ });
9325
+ }
9326
+ return chips;
9327
+ });
9279
9328
  const getInitials2 = (firstName, lastName) => {
9280
9329
  if (!firstName && !lastName) return "U";
9281
9330
  return `${firstName?.[0] || ""}${lastName?.[0] || ""}`.toUpperCase();
@@ -9285,6 +9334,9 @@ const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
9285
9334
  fetchingProfile.value = true;
9286
9335
  try {
9287
9336
  await fetchProfile();
9337
+ if (isAuthenticated.value) {
9338
+ await fetchMfaDevices();
9339
+ }
9288
9340
  } catch (err) {
9289
9341
  const errorMsg = err instanceof Error ? err.message : "Failed to load profile";
9290
9342
  errorMessage.value = errorMsg;
@@ -9499,6 +9551,7 @@ const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
9499
9551
  const handleMfaUpdated = async () => {
9500
9552
  try {
9501
9553
  await fetchProfile();
9554
+ await fetchMfaDevices();
9502
9555
  successMessage.value = "MFA settings updated successfully";
9503
9556
  emit("mfa-toggle", currentUser.value?.mfaEnabled || false);
9504
9557
  } catch (err) {
@@ -9536,7 +9589,7 @@ const _sfc_main$7 = /* @__PURE__ */ vue.defineComponent({
9536
9589
  signingOut.value = false;
9537
9590
  }
9538
9591
  };
9539
- const __returned__ = { props, emit, getSupportEmail, strandsConfig, getUrl, fetchProfile, updateProfile, changeEmail, authUser, currentSession, isAuthenticated, refreshToken, signOut, internalUser, fetchingProfile, currentUser, loading: loading2, uploading, signingOut, showPasswordChange, showEmailChange, emailChangeLoading, showMfaModal, successMessage, errorMessage, activeSessions, fileInputRef, form, emailChangeForm, passwordForm, errors, hasChanges, isPasswordFormValid, isEmailChangeFormValid, passwordLastUpdated, getInitials: getInitials2, fetchUserProfile, clearMessages, handleUpdateProfile, handlePasswordChange, handleEmailChange, triggerFileUpload, handleFileSelect, uploadAvatar, handleMfaUpdated, handleCancel, handleSignOut, get StrandsUiButton() {
9592
+ const __returned__ = { props, emit, getSupportEmail, strandsConfig, getUrl, fetchProfile, updateProfile, changeEmail, authUser, currentSession, isAuthenticated, refreshToken, signOut, mfaDevices: mfaDevices2, mfaEnabled: mfaEnabled2, activeMfaDevices, fetchMfaDevices, internalUser, fetchingProfile, currentUser, loading: loading2, uploading, signingOut, showPasswordChange, showEmailChange, emailChangeLoading, showMfaModal, successMessage, errorMessage, activeSessions, fileInputRef, form, emailChangeForm, passwordForm, errors, hasChanges, isPasswordFormValid, isEmailChangeFormValid, passwordLastUpdated, deviceTypeCounts, mfaDeviceChips, getInitials: getInitials2, fetchUserProfile, clearMessages, handleUpdateProfile, handlePasswordChange, handleEmailChange, triggerFileUpload, handleFileSelect, uploadAvatar, handleMfaUpdated, handleCancel, handleSignOut, get StrandsUiButton() {
9540
9593
  return StrandsUiButton;
9541
9594
  }, get StrandsUiInput() {
9542
9595
  return StrandsUiInput;
@@ -9589,31 +9642,37 @@ const _hoisted_21 = {
9589
9642
  class: "space-y-3 overflow-hidden"
9590
9643
  };
9591
9644
  const _hoisted_22 = { class: "p-4 bg-gray-50 rounded-xl" };
9592
- const _hoisted_23 = { class: "flex items-center justify-between gap-2" };
9593
- const _hoisted_24 = { class: "text-sm text-gray-600" };
9594
- const _hoisted_25 = { class: "p-4 bg-gray-50 rounded-xl" };
9595
- const _hoisted_26 = { class: "flex items-center justify-between gap-2" };
9596
- const _hoisted_27 = { class: "text-sm text-gray-600" };
9597
- const _hoisted_28 = {
9645
+ const _hoisted_23 = { class: "flex items-start justify-between gap-4" };
9646
+ const _hoisted_24 = { class: "flex-1" };
9647
+ const _hoisted_25 = { class: "text-sm text-gray-600 mt-1" };
9648
+ const _hoisted_26 = {
9649
+ key: 0,
9650
+ class: "flex flex-wrap gap-2 mt-3"
9651
+ };
9652
+ const _hoisted_27 = { class: "ml-1" };
9653
+ const _hoisted_28 = { class: "p-4 bg-gray-50 rounded-xl" };
9654
+ const _hoisted_29 = { class: "flex items-center justify-between gap-2" };
9655
+ const _hoisted_30 = { class: "text-sm text-gray-600" };
9656
+ const _hoisted_31 = {
9598
9657
  key: 0,
9599
9658
  class: "flex flex-col sm:flex-row gap-3 pt-6 border-t border-gray-200 animate-slide-up"
9600
9659
  };
9601
- const _hoisted_29 = {
9660
+ const _hoisted_32 = {
9602
9661
  key: 0,
9603
9662
  class: "mt-6 animate-fade-in"
9604
9663
  };
9605
- const _hoisted_30 = { class: "alert-success" };
9606
- const _hoisted_31 = { class: "flex items-start gap-3" };
9607
- const _hoisted_32 = { class: "font-medium" };
9608
- const _hoisted_33 = {
9664
+ const _hoisted_33 = { class: "alert-success" };
9665
+ const _hoisted_34 = { class: "flex items-start gap-3" };
9666
+ const _hoisted_35 = { class: "font-medium" };
9667
+ const _hoisted_36 = {
9609
9668
  key: 1,
9610
9669
  class: "mt-6 animate-fade-in"
9611
9670
  };
9612
- const _hoisted_34 = { class: "alert-error" };
9613
- const _hoisted_35 = { class: "flex items-start gap-3" };
9614
- const _hoisted_36 = { class: "font-medium" };
9615
- const _hoisted_37 = { key: 0 };
9616
- const _hoisted_38 = { class: "text-gray-400 text-sm" };
9671
+ const _hoisted_37 = { class: "alert-error" };
9672
+ const _hoisted_38 = { class: "flex items-start gap-3" };
9673
+ const _hoisted_39 = { class: "font-medium" };
9674
+ const _hoisted_40 = { key: 0 };
9675
+ const _hoisted_41 = { class: "text-gray-400 text-sm" };
9617
9676
  function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
9618
9677
  return vue.openBlock(), vue.createElementBlock("div", _hoisted_1$5, [
9619
9678
  vue.createElementVNode("div", _hoisted_2$4, [
@@ -9910,7 +9969,7 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
9910
9969
  vue.createCommentVNode(" Two-Factor Authentication "),
9911
9970
  vue.createElementVNode("div", _hoisted_22, [
9912
9971
  vue.createElementVNode("div", _hoisted_23, [
9913
- vue.createElementVNode("div", null, [
9972
+ vue.createElementVNode("div", _hoisted_24, [
9914
9973
  _cache[17] || (_cache[17] = vue.createElementVNode(
9915
9974
  "h4",
9916
9975
  { class: "font-medium text-gray-900" },
@@ -9920,16 +9979,60 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
9920
9979
  )),
9921
9980
  vue.createElementVNode(
9922
9981
  "p",
9923
- _hoisted_24,
9982
+ _hoisted_25,
9924
9983
  vue.toDisplayString($setup.currentUser?.mfaEnabled ? "Enabled" : "Add extra security to your account"),
9925
9984
  1
9926
9985
  /* TEXT */
9927
- )
9986
+ ),
9987
+ vue.createCommentVNode(" Device Type Chips "),
9988
+ $setup.mfaDeviceChips.length > 0 ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_26, [
9989
+ (vue.openBlock(true), vue.createElementBlock(
9990
+ vue.Fragment,
9991
+ null,
9992
+ vue.renderList($setup.mfaDeviceChips, (chip) => {
9993
+ return vue.openBlock(), vue.createElementBlock(
9994
+ "div",
9995
+ {
9996
+ key: chip.type,
9997
+ class: vue.normalizeClass([
9998
+ "inline-flex items-center px-2.5 py-1 rounded-lg text-xs font-medium border transition-colors",
9999
+ chip.color
10000
+ ])
10001
+ },
10002
+ [
10003
+ (vue.openBlock(), vue.createBlock(vue.resolveDynamicComponent(chip.icon), {
10004
+ size: 12,
10005
+ class: "mr-1.5"
10006
+ })),
10007
+ vue.createElementVNode(
10008
+ "span",
10009
+ null,
10010
+ vue.toDisplayString(chip.count),
10011
+ 1
10012
+ /* TEXT */
10013
+ ),
10014
+ vue.createElementVNode(
10015
+ "span",
10016
+ _hoisted_27,
10017
+ vue.toDisplayString(chip.label),
10018
+ 1
10019
+ /* TEXT */
10020
+ )
10021
+ ],
10022
+ 2
10023
+ /* CLASS */
10024
+ );
10025
+ }),
10026
+ 128
10027
+ /* KEYED_FRAGMENT */
10028
+ ))
10029
+ ])) : vue.createCommentVNode("v-if", true)
9928
10030
  ]),
9929
10031
  vue.createVNode($setup["StrandsUiButton"], {
9930
10032
  variant: $setup.currentUser?.mfaEnabled ? "secondary" : "primary",
9931
10033
  size: "sm",
9932
- onClick: _cache[9] || (_cache[9] = ($event) => $setup.showMfaModal = true)
10034
+ onClick: _cache[9] || (_cache[9] = ($event) => $setup.showMfaModal = true),
10035
+ class: "flex-shrink-0"
9933
10036
  }, {
9934
10037
  default: vue.withCtx(() => [
9935
10038
  vue.createTextVNode(
@@ -9944,8 +10047,8 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
9944
10047
  ])
9945
10048
  ]),
9946
10049
  vue.createCommentVNode(" Active Sessions "),
9947
- vue.createElementVNode("div", _hoisted_25, [
9948
- vue.createElementVNode("div", _hoisted_26, [
10050
+ vue.createElementVNode("div", _hoisted_28, [
10051
+ vue.createElementVNode("div", _hoisted_29, [
9949
10052
  vue.createElementVNode("div", null, [
9950
10053
  _cache[18] || (_cache[18] = vue.createElementVNode(
9951
10054
  "h4",
@@ -9956,7 +10059,7 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
9956
10059
  )),
9957
10060
  vue.createElementVNode(
9958
10061
  "p",
9959
- _hoisted_27,
10062
+ _hoisted_30,
9960
10063
  vue.toDisplayString($setup.activeSessions.length) + " active device(s)",
9961
10064
  1
9962
10065
  /* TEXT */
@@ -9983,7 +10086,7 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
9983
10086
  ])
9984
10087
  ]),
9985
10088
  vue.createCommentVNode(" Action Buttons - Only show when changes are made "),
9986
- $setup.hasChanges ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_28, [
10089
+ $setup.hasChanges ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_31, [
9987
10090
  vue.createVNode($setup["StrandsUiButton"], {
9988
10091
  type: "submit",
9989
10092
  variant: "primary",
@@ -10021,9 +10124,9 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
10021
10124
  /* NEED_HYDRATION */
10022
10125
  ),
10023
10126
  vue.createCommentVNode(" Success/Error Messages "),
10024
- $setup.successMessage ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_29, [
10025
- vue.createElementVNode("div", _hoisted_30, [
10026
- vue.createElementVNode("div", _hoisted_31, [
10127
+ $setup.successMessage ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_32, [
10128
+ vue.createElementVNode("div", _hoisted_33, [
10129
+ vue.createElementVNode("div", _hoisted_34, [
10027
10130
  _cache[22] || (_cache[22] = vue.createElementVNode(
10028
10131
  "svg",
10029
10132
  {
@@ -10043,7 +10146,7 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
10043
10146
  )),
10044
10147
  vue.createElementVNode(
10045
10148
  "p",
10046
- _hoisted_32,
10149
+ _hoisted_35,
10047
10150
  vue.toDisplayString($setup.successMessage),
10048
10151
  1
10049
10152
  /* TEXT */
@@ -10051,9 +10154,9 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
10051
10154
  ])
10052
10155
  ])
10053
10156
  ])) : vue.createCommentVNode("v-if", true),
10054
- $setup.errorMessage ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_33, [
10055
- vue.createElementVNode("div", _hoisted_34, [
10056
- vue.createElementVNode("div", _hoisted_35, [
10157
+ $setup.errorMessage ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_36, [
10158
+ vue.createElementVNode("div", _hoisted_37, [
10159
+ vue.createElementVNode("div", _hoisted_38, [
10057
10160
  _cache[23] || (_cache[23] = vue.createElementVNode(
10058
10161
  "svg",
10059
10162
  {
@@ -10073,7 +10176,7 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
10073
10176
  )),
10074
10177
  vue.createElementVNode(
10075
10178
  "p",
10076
- _hoisted_36,
10179
+ _hoisted_39,
10077
10180
  vue.toDisplayString($setup.errorMessage),
10078
10181
  1
10079
10182
  /* TEXT */
@@ -10104,8 +10207,8 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
10104
10207
  /* STABLE */
10105
10208
  }, 8, ["disabled"]),
10106
10209
  vue.createCommentVNode(" Need help "),
10107
- $setup.getSupportEmail() ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_37, [
10108
- vue.createElementVNode("p", _hoisted_38, [
10210
+ $setup.getSupportEmail() ? (vue.openBlock(), vue.createElementBlock("div", _hoisted_40, [
10211
+ vue.createElementVNode("p", _hoisted_41, [
10109
10212
  _cache[25] || (_cache[25] = vue.createTextVNode(
10110
10213
  " Need help? ",
10111
10214
  -1