@metropolle/design-system
Version:
Sistema de design unificado para a plataforma Metropolle
4,433 lines • 243 kB
JavaScript
'use strict';
var require$$0 = require('react');
var reactDom = require('react-dom');
var recharts = require('recharts');
var jsxRuntime = {exports: {}};
var reactJsxRuntime_production_min = {};
/**
* @license React
* react-jsx-runtime.production.min.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_production_min;
function requireReactJsxRuntime_production_min () {
if (hasRequiredReactJsxRuntime_production_min) return reactJsxRuntime_production_min;
hasRequiredReactJsxRuntime_production_min = 1;
var f=require$$0,k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:true,ref:true,__self:true,__source:true};
function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a) void 0===d[b]&&(d[b]=a[b]);return {$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}reactJsxRuntime_production_min.Fragment=l;reactJsxRuntime_production_min.jsx=q;reactJsxRuntime_production_min.jsxs=q;
return reactJsxRuntime_production_min;
}
var reactJsxRuntime_development = {};
/**
* @license React
* react-jsx-runtime.development.js
*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var hasRequiredReactJsxRuntime_development;
function requireReactJsxRuntime_development () {
if (hasRequiredReactJsxRuntime_development) return reactJsxRuntime_development;
hasRequiredReactJsxRuntime_development = 1;
if (process.env.NODE_ENV !== "production") {
(function() {
var React = require$$0;
// ATTENTION
// When adding new symbols to this file,
// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
// The Symbol used to tag the ReactElement-like types.
var REACT_ELEMENT_TYPE = Symbol.for('react.element');
var REACT_PORTAL_TYPE = Symbol.for('react.portal');
var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');
var REACT_PROFILER_TYPE = Symbol.for('react.profiler');
var REACT_PROVIDER_TYPE = Symbol.for('react.provider');
var REACT_CONTEXT_TYPE = Symbol.for('react.context');
var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
var REACT_MEMO_TYPE = Symbol.for('react.memo');
var REACT_LAZY_TYPE = Symbol.for('react.lazy');
var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen');
var MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = '@@iterator';
function getIteratorFn(maybeIterable) {
if (maybeIterable === null || typeof maybeIterable !== 'object') {
return null;
}
var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
if (typeof maybeIterator === 'function') {
return maybeIterator;
}
return null;
}
var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
function error(format) {
{
{
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
args[_key2 - 1] = arguments[_key2];
}
printWarning('error', format, args);
}
}
}
function printWarning(level, format, args) {
// When changing this logic, you might want to also
// update consoleWithStackDev.www.js as well.
{
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
var stack = ReactDebugCurrentFrame.getStackAddendum();
if (stack !== '') {
format += '%s';
args = args.concat([stack]);
} // eslint-disable-next-line react-internal/safe-string-coercion
var argsWithFormat = args.map(function (item) {
return String(item);
}); // Careful: RN currently depends on this prefix
argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it
// breaks IE9: https://github.com/facebook/react/issues/13610
// eslint-disable-next-line react-internal/no-production-logging
Function.prototype.apply.call(console[level], console, argsWithFormat);
}
}
// -----------------------------------------------------------------------------
var enableScopeAPI = false; // Experimental Create Event Handle API.
var enableCacheElement = false;
var enableTransitionTracing = false; // No known bugs, but needs performance testing
var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber
// stuff. Intended to enable React core members to more easily debug scheduling
// issues in DEV builds.
var enableDebugTracing = false; // Track which Fiber(s) schedule render work.
var REACT_MODULE_REFERENCE;
{
REACT_MODULE_REFERENCE = Symbol.for('react.module.reference');
}
function isValidElementType(type) {
if (typeof type === 'string' || typeof type === 'function') {
return true;
} // Note: typeof might be other than 'symbol' or 'number' (e.g. if it's a polyfill).
if (type === REACT_FRAGMENT_TYPE || type === REACT_PROFILER_TYPE || enableDebugTracing || type === REACT_STRICT_MODE_TYPE || type === REACT_SUSPENSE_TYPE || type === REACT_SUSPENSE_LIST_TYPE || enableLegacyHidden || type === REACT_OFFSCREEN_TYPE || enableScopeAPI || enableCacheElement || enableTransitionTracing ) {
return true;
}
if (typeof type === 'object' && type !== null) {
if (type.$$typeof === REACT_LAZY_TYPE || type.$$typeof === REACT_MEMO_TYPE || type.$$typeof === REACT_PROVIDER_TYPE || type.$$typeof === REACT_CONTEXT_TYPE || type.$$typeof === REACT_FORWARD_REF_TYPE || // This needs to include all possible module reference object
// types supported by any Flight configuration anywhere since
// we don't know which Flight build this will end up being used
// with.
type.$$typeof === REACT_MODULE_REFERENCE || type.getModuleId !== undefined) {
return true;
}
}
return false;
}
function getWrappedName(outerType, innerType, wrapperName) {
var displayName = outerType.displayName;
if (displayName) {
return displayName;
}
var functionName = innerType.displayName || innerType.name || '';
return functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName;
} // Keep in sync with react-reconciler/getComponentNameFromFiber
function getContextName(type) {
return type.displayName || 'Context';
} // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.
function getComponentNameFromType(type) {
if (type == null) {
// Host root, text node or just invalid type.
return null;
}
{
if (typeof type.tag === 'number') {
error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.');
}
}
if (typeof type === 'function') {
return type.displayName || type.name || null;
}
if (typeof type === 'string') {
return type;
}
switch (type) {
case REACT_FRAGMENT_TYPE:
return 'Fragment';
case REACT_PORTAL_TYPE:
return 'Portal';
case REACT_PROFILER_TYPE:
return 'Profiler';
case REACT_STRICT_MODE_TYPE:
return 'StrictMode';
case REACT_SUSPENSE_TYPE:
return 'Suspense';
case REACT_SUSPENSE_LIST_TYPE:
return 'SuspenseList';
}
if (typeof type === 'object') {
switch (type.$$typeof) {
case REACT_CONTEXT_TYPE:
var context = type;
return getContextName(context) + '.Consumer';
case REACT_PROVIDER_TYPE:
var provider = type;
return getContextName(provider._context) + '.Provider';
case REACT_FORWARD_REF_TYPE:
return getWrappedName(type, type.render, 'ForwardRef');
case REACT_MEMO_TYPE:
var outerName = type.displayName || null;
if (outerName !== null) {
return outerName;
}
return getComponentNameFromType(type.type) || 'Memo';
case REACT_LAZY_TYPE:
{
var lazyComponent = type;
var payload = lazyComponent._payload;
var init = lazyComponent._init;
try {
return getComponentNameFromType(init(payload));
} catch (x) {
return null;
}
}
// eslint-disable-next-line no-fallthrough
}
}
return null;
}
var assign = Object.assign;
// Helpers to patch console.logs to avoid logging during side-effect free
// replaying on render function. This currently only patches the object
// lazily which won't cover if the log function was extracted eagerly.
// We could also eagerly patch the method.
var disabledDepth = 0;
var prevLog;
var prevInfo;
var prevWarn;
var prevError;
var prevGroup;
var prevGroupCollapsed;
var prevGroupEnd;
function disabledLog() {}
disabledLog.__reactDisabledLog = true;
function disableLogs() {
{
if (disabledDepth === 0) {
/* eslint-disable react-internal/no-production-logging */
prevLog = console.log;
prevInfo = console.info;
prevWarn = console.warn;
prevError = console.error;
prevGroup = console.group;
prevGroupCollapsed = console.groupCollapsed;
prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099
var props = {
configurable: true,
enumerable: true,
value: disabledLog,
writable: true
}; // $FlowFixMe Flow thinks console is immutable.
Object.defineProperties(console, {
info: props,
log: props,
warn: props,
error: props,
group: props,
groupCollapsed: props,
groupEnd: props
});
/* eslint-enable react-internal/no-production-logging */
}
disabledDepth++;
}
}
function reenableLogs() {
{
disabledDepth--;
if (disabledDepth === 0) {
/* eslint-disable react-internal/no-production-logging */
var props = {
configurable: true,
enumerable: true,
writable: true
}; // $FlowFixMe Flow thinks console is immutable.
Object.defineProperties(console, {
log: assign({}, props, {
value: prevLog
}),
info: assign({}, props, {
value: prevInfo
}),
warn: assign({}, props, {
value: prevWarn
}),
error: assign({}, props, {
value: prevError
}),
group: assign({}, props, {
value: prevGroup
}),
groupCollapsed: assign({}, props, {
value: prevGroupCollapsed
}),
groupEnd: assign({}, props, {
value: prevGroupEnd
})
});
/* eslint-enable react-internal/no-production-logging */
}
if (disabledDepth < 0) {
error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.');
}
}
}
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
var prefix;
function describeBuiltInComponentFrame(name, source, ownerFn) {
{
if (prefix === undefined) {
// Extract the VM specific prefix used by each line.
try {
throw Error();
} catch (x) {
var match = x.stack.trim().match(/\n( *(at )?)/);
prefix = match && match[1] || '';
}
} // We use the prefix to ensure our stacks line up with native stack frames.
return '\n' + prefix + name;
}
}
var reentry = false;
var componentFrameCache;
{
var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
componentFrameCache = new PossiblyWeakMap();
}
function describeNativeComponentFrame(fn, construct) {
// If something asked for a stack inside a fake render, it should get ignored.
if ( !fn || reentry) {
return '';
}
{
var frame = componentFrameCache.get(fn);
if (frame !== undefined) {
return frame;
}
}
var control;
reentry = true;
var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined.
Error.prepareStackTrace = undefined;
var previousDispatcher;
{
previousDispatcher = ReactCurrentDispatcher.current; // Set the dispatcher in DEV because this might be call in the render function
// for warnings.
ReactCurrentDispatcher.current = null;
disableLogs();
}
try {
// This should throw.
if (construct) {
// Something should be setting the props in the constructor.
var Fake = function () {
throw Error();
}; // $FlowFixMe
Object.defineProperty(Fake.prototype, 'props', {
set: function () {
// We use a throwing setter instead of frozen or non-writable props
// because that won't throw in a non-strict mode function.
throw Error();
}
});
if (typeof Reflect === 'object' && Reflect.construct) {
// We construct a different control for this case to include any extra
// frames added by the construct call.
try {
Reflect.construct(Fake, []);
} catch (x) {
control = x;
}
Reflect.construct(fn, [], Fake);
} else {
try {
Fake.call();
} catch (x) {
control = x;
}
fn.call(Fake.prototype);
}
} else {
try {
throw Error();
} catch (x) {
control = x;
}
fn();
}
} catch (sample) {
// This is inlined manually because closure doesn't do it for us.
if (sample && control && typeof sample.stack === 'string') {
// This extracts the first frame from the sample that isn't also in the control.
// Skipping one frame that we assume is the frame that calls the two.
var sampleLines = sample.stack.split('\n');
var controlLines = control.stack.split('\n');
var s = sampleLines.length - 1;
var c = controlLines.length - 1;
while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) {
// We expect at least one stack frame to be shared.
// Typically this will be the root most one. However, stack frames may be
// cut off due to maximum stack limits. In this case, one maybe cut off
// earlier than the other. We assume that the sample is longer or the same
// and there for cut off earlier. So we should find the root most frame in
// the sample somewhere in the control.
c--;
}
for (; s >= 1 && c >= 0; s--, c--) {
// Next we find the first one that isn't the same which should be the
// frame that called our sample function and the control.
if (sampleLines[s] !== controlLines[c]) {
// In V8, the first line is describing the message but other VMs don't.
// If we're about to return the first line, and the control is also on the same
// line, that's a pretty good indicator that our sample threw at same line as
// the control. I.e. before we entered the sample frame. So we ignore this result.
// This can happen if you passed a class to function component, or non-function.
if (s !== 1 || c !== 1) {
do {
s--;
c--; // We may still have similar intermediate frames from the construct call.
// The next one that isn't the same should be our match though.
if (c < 0 || sampleLines[s] !== controlLines[c]) {
// V8 adds a "new" prefix for native classes. Let's remove it to make it prettier.
var _frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "<anonymous>"
// but we have a user-provided "displayName"
// splice it in to make the stack more readable.
if (fn.displayName && _frame.includes('<anonymous>')) {
_frame = _frame.replace('<anonymous>', fn.displayName);
}
{
if (typeof fn === 'function') {
componentFrameCache.set(fn, _frame);
}
} // Return the line we found.
return _frame;
}
} while (s >= 1 && c >= 0);
}
break;
}
}
}
} finally {
reentry = false;
{
ReactCurrentDispatcher.current = previousDispatcher;
reenableLogs();
}
Error.prepareStackTrace = previousPrepareStackTrace;
} // Fallback to just using the name if we couldn't make it throw.
var name = fn ? fn.displayName || fn.name : '';
var syntheticFrame = name ? describeBuiltInComponentFrame(name) : '';
{
if (typeof fn === 'function') {
componentFrameCache.set(fn, syntheticFrame);
}
}
return syntheticFrame;
}
function describeFunctionComponentFrame(fn, source, ownerFn) {
{
return describeNativeComponentFrame(fn, false);
}
}
function shouldConstruct(Component) {
var prototype = Component.prototype;
return !!(prototype && prototype.isReactComponent);
}
function describeUnknownElementTypeFrameInDEV(type, source, ownerFn) {
if (type == null) {
return '';
}
if (typeof type === 'function') {
{
return describeNativeComponentFrame(type, shouldConstruct(type));
}
}
if (typeof type === 'string') {
return describeBuiltInComponentFrame(type);
}
switch (type) {
case REACT_SUSPENSE_TYPE:
return describeBuiltInComponentFrame('Suspense');
case REACT_SUSPENSE_LIST_TYPE:
return describeBuiltInComponentFrame('SuspenseList');
}
if (typeof type === 'object') {
switch (type.$$typeof) {
case REACT_FORWARD_REF_TYPE:
return describeFunctionComponentFrame(type.render);
case REACT_MEMO_TYPE:
// Memo may contain any component type so we recursively resolve it.
return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);
case REACT_LAZY_TYPE:
{
var lazyComponent = type;
var payload = lazyComponent._payload;
var init = lazyComponent._init;
try {
// Lazy may contain any component type so we recursively resolve it.
return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn);
} catch (x) {}
}
}
}
return '';
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
var loggedTypeFailures = {};
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
function setCurrentlyValidatingElement(element) {
{
if (element) {
var owner = element._owner;
var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);
ReactDebugCurrentFrame.setExtraStackFrame(stack);
} else {
ReactDebugCurrentFrame.setExtraStackFrame(null);
}
}
}
function checkPropTypes(typeSpecs, values, location, componentName, element) {
{
// $FlowFixMe This is okay but Flow doesn't know it.
var has = Function.call.bind(hasOwnProperty);
for (var typeSpecName in typeSpecs) {
if (has(typeSpecs, typeSpecName)) {
var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to
// fail the render phase where it didn't fail before. So we log it.
// After these have been cleaned up, we'll let them throw.
try {
// This is intentionally an invariant that gets caught. It's the same
// behavior as without this statement except with a better message.
if (typeof typeSpecs[typeSpecName] !== 'function') {
// eslint-disable-next-line react-internal/prod-error-codes
var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.');
err.name = 'Invariant Violation';
throw err;
}
error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED');
} catch (ex) {
error$1 = ex;
}
if (error$1 && !(error$1 instanceof Error)) {
setCurrentlyValidatingElement(element);
error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1);
setCurrentlyValidatingElement(null);
}
if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error$1.message] = true;
setCurrentlyValidatingElement(element);
error('Failed %s type: %s', location, error$1.message);
setCurrentlyValidatingElement(null);
}
}
}
}
}
var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare
function isArray(a) {
return isArrayImpl(a);
}
/*
* The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol
* and Temporal.* types. See https://github.com/facebook/react/pull/22064.
*
* The functions in this module will throw an easier-to-understand,
* easier-to-debug exception with a clear errors message message explaining the
* problem. (Instead of a confusing exception thrown inside the implementation
* of the `value` object).
*/
// $FlowFixMe only called in DEV, so void return is not possible.
function typeName(value) {
{
// toStringTag is needed for namespaced types like Temporal.Instant
var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag;
var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object';
return type;
}
} // $FlowFixMe only called in DEV, so void return is not possible.
function willCoercionThrow(value) {
{
try {
testStringCoercion(value);
return false;
} catch (e) {
return true;
}
}
}
function testStringCoercion(value) {
// If you ended up here by following an exception call stack, here's what's
// happened: you supplied an object or symbol value to React (as a prop, key,
// DOM attribute, CSS property, string ref, etc.) and when React tried to
// coerce it to a string using `'' + value`, an exception was thrown.
//
// The most common types that will cause this exception are `Symbol` instances
// and Temporal objects like `Temporal.Instant`. But any object that has a
// `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this
// exception. (Library authors do this to prevent users from using built-in
// numeric operators like `+` or comparison operators like `>=` because custom
// methods are needed to perform accurate arithmetic or comparison.)
//
// To fix the problem, coerce this object or symbol value to a string before
// passing it to React. The most reliable way is usually `String(value)`.
//
// To find which value is throwing, check the browser or debugger console.
// Before this exception was thrown, there should be `console.error` output
// that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the
// problem and how that type was used: key, atrribute, input value prop, etc.
// In most cases, this console output also shows the component and its
// ancestor components where the exception happened.
//
// eslint-disable-next-line react-internal/safe-string-coercion
return '' + value;
}
function checkKeyStringCoercion(value) {
{
if (willCoercionThrow(value)) {
error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value));
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
}
}
}
var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
var RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true
};
var specialPropKeyWarningShown;
var specialPropRefWarningShown;
function hasValidRef(config) {
{
if (hasOwnProperty.call(config, 'ref')) {
var getter = Object.getOwnPropertyDescriptor(config, 'ref').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.ref !== undefined;
}
function hasValidKey(config) {
{
if (hasOwnProperty.call(config, 'key')) {
var getter = Object.getOwnPropertyDescriptor(config, 'key').get;
if (getter && getter.isReactWarning) {
return false;
}
}
}
return config.key !== undefined;
}
function warnIfStringRefCannotBeAutoConverted(config, self) {
{
if (typeof config.ref === 'string' && ReactCurrentOwner.current && self) ;
}
}
function defineKeyPropWarningGetter(props, displayName) {
{
var warnAboutAccessingKey = function () {
if (!specialPropKeyWarningShown) {
specialPropKeyWarningShown = true;
error('%s: `key` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName);
}
};
warnAboutAccessingKey.isReactWarning = true;
Object.defineProperty(props, 'key', {
get: warnAboutAccessingKey,
configurable: true
});
}
}
function defineRefPropWarningGetter(props, displayName) {
{
var warnAboutAccessingRef = function () {
if (!specialPropRefWarningShown) {
specialPropRefWarningShown = true;
error('%s: `ref` is not a prop. Trying to access it will result ' + 'in `undefined` being returned. If you need to access the same ' + 'value within the child component, you should pass it as a different ' + 'prop. (https://reactjs.org/link/special-props)', displayName);
}
};
warnAboutAccessingRef.isReactWarning = true;
Object.defineProperty(props, 'ref', {
get: warnAboutAccessingRef,
configurable: true
});
}
}
/**
* Factory method to create a new React element. This no longer adheres to
* the class pattern, so do not use new to call it. Also, instanceof check
* will not work. Instead test $$typeof field against Symbol.for('react.element') to check
* if something is a React Element.
*
* @param {*} type
* @param {*} props
* @param {*} key
* @param {string|object} ref
* @param {*} owner
* @param {*} self A *temporary* helper to detect places where `this` is
* different from the `owner` when React.createElement is called, so that we
* can warn. We want to get rid of owner and replace string `ref`s with arrow
* functions, and as long as `this` and owner are the same, there will be no
* change in behavior.
* @param {*} source An annotation object (added by a transpiler or otherwise)
* indicating filename, line number, and/or other information.
* @internal
*/
var ReactElement = function (type, key, ref, self, source, owner, props) {
var element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
{
// The validation flag is currently mutative. We put it on
// an external backing store so that we can freeze the whole object.
// This can be replaced with a WeakMap once they are implemented in
// commonly used development environments.
element._store = {}; // To make comparing ReactElements easier for testing purposes, we make
// the validation flag non-enumerable (where possible, which should
// include every environment we run tests in), so the test framework
// ignores it.
Object.defineProperty(element._store, 'validated', {
configurable: false,
enumerable: false,
writable: true,
value: false
}); // self and source are DEV only properties.
Object.defineProperty(element, '_self', {
configurable: false,
enumerable: false,
writable: false,
value: self
}); // Two elements created in two different places should be considered
// equal for testing purposes and therefore we hide it from enumeration.
Object.defineProperty(element, '_source', {
configurable: false,
enumerable: false,
writable: false,
value: source
});
if (Object.freeze) {
Object.freeze(element.props);
Object.freeze(element);
}
}
return element;
};
/**
* https://github.com/reactjs/rfcs/pull/107
* @param {*} type
* @param {object} props
* @param {string} key
*/
function jsxDEV(type, config, maybeKey, source, self) {
{
var propName; // Reserved names are extracted
var props = {};
var key = null;
var ref = null; // Currently, key can be spread in as a prop. This causes a potential
// issue if key is also explicitly declared (ie. <div {...props} key="Hi" />
// or <div key="Hi" {...props} /> ). We want to deprecate key spread,
// but as an intermediary step, we will use jsxDEV for everything except
// <div {...props} key="Hi" />, because we aren't currently able to tell if
// key is explicitly declared to be undefined or not.
if (maybeKey !== undefined) {
{
checkKeyStringCoercion(maybeKey);
}
key = '' + maybeKey;
}
if (hasValidKey(config)) {
{
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
if (hasValidRef(config)) {
ref = config.ref;
warnIfStringRefCannotBeAutoConverted(config, self);
} // Remaining properties are added to a new props object
for (propName in config) {
if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
} // Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (key || ref) {
var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
return ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
}
}
var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;
var ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame;
function setCurrentlyValidatingElement$1(element) {
{
if (element) {
var owner = element._owner;
var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);
ReactDebugCurrentFrame$1.setExtraStackFrame(stack);
} else {
ReactDebugCurrentFrame$1.setExtraStackFrame(null);
}
}
}
var propTypesMisspellWarningShown;
{
propTypesMisspellWarningShown = false;
}
/**
* Verifies the object is a ReactElement.
* See https://reactjs.org/docs/react-api.html#isvalidelement
* @param {?object} object
* @return {boolean} True if `object` is a ReactElement.
* @final
*/
function isValidElement(object) {
{
return typeof object === 'object' && object !== null && object.$$typeof === REACT_ELEMENT_TYPE;
}
}
function getDeclarationErrorAddendum() {
{
if (ReactCurrentOwner$1.current) {
var name = getComponentNameFromType(ReactCurrentOwner$1.current.type);
if (name) {
return '\n\nCheck the render method of `' + name + '`.';
}
}
return '';
}
}
function getSourceInfoErrorAddendum(source) {
{
return '';
}
}
/**
* Warn if there's no key explicitly set on dynamic arrays of children or
* object keys are not valid. This allows us to keep track of children between
* updates.
*/
var ownerHasKeyUseWarning = {};
function getCurrentComponentErrorInfo(parentType) {
{
var info = getDeclarationErrorAddendum();
if (!info) {
var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;
if (parentName) {
info = "\n\nCheck the top-level render call using <" + parentName + ">.";
}
}
return info;
}
}
/**
* Warn if the element doesn't have an explicit key assigned to it.
* This element is in an array. The array could grow and shrink or be
* reordered. All children that haven't already been validated are required to
* have a "key" property assigned to it. Error statuses are cached so a warning
* will only be shown once.
*
* @internal
* @param {ReactElement} element Element that requires a key.
* @param {*} parentType element's parent's type.
*/
function validateExplicitKey(element, parentType) {
{
if (!element._store || element._store.validated || element.key != null) {
return;
}
element._store.validated = true;
var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
return;
}
ownerHasKeyUseWarning[currentComponentErrorInfo] = true; // Usually the current owner is the offender, but if it accepts children as a
// property, it may be the creator of the child that's responsible for
// assigning it a key.
var childOwner = '';
if (element && element._owner && element._owner !== ReactCurrentOwner$1.current) {
// Give the component that originally created this child.
childOwner = " It was passed a child from " + getComponentNameFromType(element._owner.type) + ".";
}
setCurrentlyValidatingElement$1(element);
error('Each child in a list should have a unique "key" prop.' + '%s%s See https://reactjs.org/link/warning-keys for more information.', currentComponentErrorInfo, childOwner);
setCurrentlyValidatingElement$1(null);
}
}
/**
* Ensure that every element either is passed in a static location, in an
* array with an explicit keys property defined, or in an object literal
* with valid key property.
*
* @internal
* @param {ReactNode} node Statically passed child of any type.
* @param {*} parentType node's parent's type.
*/
function validateChildKeys(node, parentType) {
{
if (typeof node !== 'object') {
return;
}
if (isArray(node)) {
for (var i = 0; i < node.length; i++) {
var child = node[i];
if (isValidElement(child)) {
validateExplicitKey(child, parentType);
}
}
} else if (isValidElement(node)) {
// This element was passed in a valid location.
if (node._store) {
node._store.validated = true;
}
} else if (node) {
var iteratorFn = getIteratorFn(node);
if (typeof iteratorFn === 'function') {
// Entry iterators used to provide implicit keys,
// but now we print a separate warning for them later.
if (iteratorFn !== node.entries) {
var iterator = iteratorFn.call(node);
var step;
while (!(step = iterator.next()).done) {
if (isValidElement(step.value)) {
validateExplicitKey(step.value, parentType);
}
}
}
}
}
}
}
/**
* Given an element, validate that its props follow the propTypes definition,
* provided by the type.
*
* @param {ReactElement} element
*/
function validatePropTypes(element) {
{
var type = element.type;
if (type === null || type === undefined || typeof type === 'string') {
return;
}
var propTypes;
if (typeof type === 'function') {
propTypes = type.propTypes;
} else if (typeof type === 'object' && (type.$$typeof === REACT_FORWARD_REF_TYPE || // Note: Memo only checks outer props here.
// Inner props are checked in the reconciler.
type.$$typeof === REACT_MEMO_TYPE)) {
propTypes = type.propTypes;
} else {
return;
}
if (propTypes) {
// Intentionally inside to avoid triggering lazy initializers:
var name = getComponentNameFromType(type);
checkPropTypes(propTypes, element.props, 'prop', name, element);
} else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
propTypesMisspellWarningShown = true; // Intentionally inside to avoid triggering lazy initializers:
var _name = getComponentNameFromType(type);
error('Component %s declared `PropTypes` instead of `propTypes`. Did you misspell the property assignment?', _name || 'Unknown');
}
if (typeof type.getDefaultProps === 'function' && !type.getDefaultProps.isReactClassApproved) {
error('getDefaultProps is only used on classic React.createClass ' + 'definitions. Use a static property named `defaultProps` instead.');
}
}
}
/**
* Given a fragment, validate that it can only be provided with fragment props
* @param {ReactElement} fragment
*/
function validateFragmentProps(fragment) {
{
var keys = Object.keys(fragment.props);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (key !== 'children' && key !== 'key') {
setCurrentlyValidatingElement$1(fragment);
error('Invalid prop `%s` supplied to `React.Fragment`. ' + 'React.Fragment can only have `key` and `children` props.', key);
setCurrentlyValidatingElement$1(null);
break;
}
}
if (fragment.ref !== null) {
setCurrentlyValidatingElement$1(fragment);
error('Invalid attribute `ref` supplied to `React.Fragment`.');
setCurrentlyValidatingElement$1(null);
}
}
}
var didWarnAboutKeySpread = {};
function jsxWithValidation(type, props, key, isStaticChildren, source, self) {
{
var validType = isValidElementType(type); // We warn in this case but don't throw. We expect the element creation to
// succeed and there will likely be errors in render.
if (!validType) {
var info = '';
if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports.";
}
var sourceInfo = getSourceInfoErrorAddendum();
if (sourceInfo) {
info += sourceInfo;
} else {
info += getDeclarationErrorAddendum();
}
var typeString;
if (type === null) {
typeString = 'null';
} else if (isArray(type)) {
typeString = 'array';
} else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
typeString = "<" + (getComponentNameFromType(type.type) || 'Unknown') + " />";
info = ' Did you accidentally export a JSX literal instead of a component?';
} else {
typeString = typeof type;
}
error('React.jsx: type is invalid -- expected a string (for ' + 'built-in components) or a class/function (for composite ' + 'components) but got: %s.%s', typeString, info);
}
var element = jsxDEV(type, props, key, source, self); // The result can be nullish if a mock or a custom function is used.
// TODO: Drop this when these are no longer allowed as the type argument.
if (element == null) {
return element;
} // Skip key warning if the type isn't valid since our key validation logic
// doesn't expect a non-string/function type and can throw confusing errors.
// We don't want exception behavior to differ between dev and prod.
// (Rendering will throw with a helpful message and as soon as the type is
// fixed, the key warnings will appear.)
if (validType) {
var children = props.children;
if (children !== undefined) {
if (isStaticChildren) {
if (isArray(children)) {
for (var i = 0; i < children.length; i++) {
validateChildKeys(children[i], type);
}
if (Object.freeze) {
Object.freeze(children);
}
} else {
error('React.jsx: Static children should always be an array. ' + 'You are likely explicitly calling React.jsxs or React.jsxDEV. ' + 'Use the Babel transform instead.');
}
} else {
validateChildKeys(children, type);
}
}
}
{
if (hasOwnProperty.call(props, 'key')) {
var componentName = getComponentNameFromType(type);
var keys = Object.keys(props).filter(function (k) {
return k !== 'key';
});
var beforeExample = keys.length > 0 ? '{key: someKey, ' + keys.join(': ..., ') + ': ...}' : '{key: someKey}';
if (!didWarnAboutKeySpread[componentName + beforeExample]) {
var afterExample = keys.length > 0 ? '{' + keys.join(': ..., ') + ': ...}' : '{}';
error('A props object containing a "key" prop is being spread into JSX:\n' + ' let props = %s;\n' + ' <%s {...props} />\n' + 'React keys must be passed directly to JSX without using spread:\n' + ' let props = %s;\n' + ' <%s key={someKey} {...props} />', beforeExample, componentName, afterExample, componentName);
didWarnAboutKeySpread[componentName + beforeExample] = true;
}
}
}
if (type === REACT_FRAGMENT_TYPE) {
validateFragmentProps(element);
} else {
validatePropTypes(element);
}
return element;
}
} // These two functions exist to still get child warnings in dev
// even with the prod transform. This means that jsxDEV is purely
// opt-in behavior for better messages but that we won't stop
// giving you warnings if you use production apis.
function jsxWithValidationStatic(type, props, key) {
{
return jsxWithValidation(type, props, key, true);
}
}
function jsxWithValidationDynamic(type, props, key) {
{
return jsxWithValidation(type, props, key, false);
}
}
var jsx = jsxWithValidationDynamic ; // we may want to special case jsxs internally to take advantage of static children.
// for now we can ship identical prod functions
var jsxs = jsxWithValidationStatic ;
reactJsxRuntime_development.Fragment = REACT_FRAGMENT_TYPE;
reactJsxRuntime_development.jsx = jsx;
reactJsxRuntime_development.jsxs = jsxs;
})();
}
return reactJsxRuntime_development;
}
if (process.env.NODE_ENV === 'production') {
jsxRuntime.exports = requireReactJsxRuntime_production_min();
} else {
jsxRuntime.exports = requireReactJsxRuntime_development();
}
var jsxRuntimeExports = jsxRuntime.exports;
/**
* Utility function to merge CSS class names
* Simple implementation for className concatenation
*/
function cn(...classes) {
return classes
.filter(Boolean)
.join(' ')
.trim();
}
/**
* Glass Card Component
*
* Componente de cartão com efeito glassmorphism ou Liquid Glass (iOS 26 style).
*
* @example
* ```tsx
* // Liquid Glass (novo padrão)
* <GlassCard glassStyle="liquid" intensity="md">
* Content here
* </GlassCard>
*
* // Glassmorphism tradicional (retrocompatível)
* <GlassCard glassStyle="glass" variant="dark">
* Content here
* </GlassCard>
* ```
*/
const GlassCard = require$$0.forwardRef(({ glassStyle = 'liquid', intensity = 'md', theme, variant = 'light', blur, opacity, children, className, enableHover = true, cardVariant = 'default', style, ...props }, ref) => {
// Resolve theme from new prop or deprecated variant
const resolvedTheme = theme ?? variant;
// =====================
// LIQUID GLASS MODE
// =====================
if (glassStyle === 'liquid') {
// PROC-007: Card variant styles (Meridian: flat, zero-border, opaque).
// 'highlight' affords via a left accent bar + flat shadow (no colored glow).
// 'subtle' uses an opaque secondary surface instead of translucent glass.
const variantStyles = {
default: {},
highlight: {
borderLeft: '3px solid var(--mds-color-accent, #2563eb)',
boxShadow: 'var(--mds-liquid-shadow-raised, 0 4px 14px -8px rgba(0, 0, 0, 0.7))'
},
subtle: {
background: 'var(--mds-liquid-bg-glass, #1C1C20)',
border: '1px solid var(--mds-liquid-border-subtle, transparent)'
}
};
return (jsxRuntimeExports.jsx("div", { ref: ref, className: cn('mds-liquid-glass', `mds-liquid-glass--${intensity}`, `mds-liquid-glass--${cardVariant}`, !enableHover && 'mds-liquid-glass--no-hover', className), style: { ...variantStyles[cardVariant], ...style }, ...props, children: children }));
}
// =====================
// GLASS MODE (legacy) — flattened to Meridian
// =====================
// Use CSS classes for base styles to avoid hydration mismatches
const baseStyles = {
position: 'relative'
};
// Only apply custom styles for non-default values.
const customStyles = {};
// `opacity` still maps to an OPAQUE surface override (no translucency):
// a non-default opacity simply requests an explicit surface color.
if (opacity !== undefined) {
customStyles.background = resolvedTheme === 'light'
? 'var(--mds-liquid-bg-card, #FFFFFF)'
: 'var(--mds-liquid-bg-card, #1C1C20)';
}
// Meridian: NO transform-based hover (no translateY/scale). Mouse callbacks
// from the consumer are still forwarded; visual hover (if any) is handled by
// the .mds-glass-card CSS class via tokens.
const handleMouseEnter = (e) => {
props.onMouseEnter?.(e);
};
const handleMouseLeave = (e) => {
props.onMouseLeave?.(e);
};
return (jsxRuntimeExports.jsx("div", { ref: ref, className: cn('mds-glass-card', `mds-glass-card--${resolvedTheme}`, !enableHover && 'mds-glass-card--no-hover', className), style: {
...baseStyles,
...customStyles,
...style
}, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, ...props, children: children }));
});
GlassCard.displayName = 'GlassCard';
const SIZE_PRESETS = {
xs: { width: 20, height: 20, fontSize: 8, strokeWidth: 1.5 },
sm: { width: 28, height: 28, fontSize: 10, strokeWidth: 1.8 },
md: { width: 40, height: 40, fontSize: 14, strokeWidth: 2 },
lg: { width: 56, height: 56, fontSize: 18, strokeWidth: 2.2 },
xl: { width: 88, height: 88, fontSize: 10, strokeWidth: 2.5 },
};
function getSizeConfig(size) {
if (typeof size === 'number') {
const ratio = size / 40;
return {
width: size,
height: size,
fontSize: Math.round(14 * ratio),
strokeWidth: Math.max(1.5, 2 * ratio),
};
}
return SIZE_PRESETS[size] || SIZE_PRESETS.md;
}
// =============================================================================
// Shield Path — outline style (same as topbar VerificationShield)
// =============================================================================
const SHIELD_PATH = 'M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z';
// =============================================================================
// Component
// =============================================================================
function VerificationShield({ score, size = 'md', className = '', ariaLabel, }) {
const config = require$$0.useMemo(() => getSizeConfig(size), [size]);
const displayScore = Math.max(0, Math.min(100, Math.round(score)));
const label = ariaLabel || `Verification score: ${displayScore}`;
return (jsxRuntimeExports.jsxs("svg", { width: config.width, height: config.height, viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: `mds-verification-shield ${className}`, role: "img", "aria-label": label, children: [jsxRuntimeExports.jsx("path", { d: SHIELD_PATH, fill: "none", stroke: "currentColor", strokeWidth: config.strokeWidth, className: "mds-verification-shield__outline" }), displayScore > 0 && (jsxRuntimeExports.jsx("text", { x: "12", y: "13", textAnchor: "middle", dominantBaseline: "middle", fontSize: config.fontSize, fontWeight: "500", fontFamily: "system-ui, -apple-system, sans-serif", fill: "currentColor", className: "mds-verification-shield__text", children: displayScore }))] }));
}
/**
* Y-domain helpers for MetricsEvolutionChart (pure — unit-tested without recharts).
*/
/**
* Coerce Dynamo/API metric fields to finite numbers (JSON numbers, numeric strings).
* Non-numeric → null (gap), never silent 0.
*/
function asMetricNumber(v) {
if (typeof v === 'number' && Number.isFinite(v))
return v;
if (typeof v === 'string' && v.trim() !== '') {
const n = Number(v);
if (Number.isFinite(n))
return n;
}
return null;
}
/**
* Y domain for percentage mode so modest relative changes stay readable.
* KPI sparklines scale to local min/max; a fixed [0, 100] domain made small
* absolute drops (e.g. 320→315 followers ≈ 98%) look completely flat.
*
* - Growth-from-zero / wide band → keep full [0, 100]
* - Narrow band near the top → zoom to data with padding (sparkline-like)
*/
function computeNormalizedYDomain(points, activeKeys) {
let min = Infinity;
let max = -Infinity;
for (const p of points) {
for (const key of activeKeys) {
for (const field of [`${key}Pct`, `${key}HoldPct`]) {
const v = p[field];
if (typeof v === 'number' && Number.isFinite(v)) {
if (v < min)
min = v;
if (v > max)
max = v;
}
}
}
}
if (!Number.isFinite(min) || !Number.isFinite(max))
return [0, 100];
// Wide swing or near-zero floor → full scale (preserve magnitude context)
const span = Math.max(max - min, 0);
if (min <= 8 || span >= 35) {
return [0, 100];
}
// Zoom to band with padding so a few-% drop still paints a clear step
const pad = Math.max(span * 0.25, 4);
let lo = Math.floor(min - pad);
let hi = Math.ceil(max + pad);
lo = Math.max(0, lo);
hi = Math.min(100, Math.max(hi, lo + 8));
if (hi - lo < 8) {
const mid = (lo + hi) / 2;
lo = Math.max(0, Math.floor(mid - 4));
hi = Math.min(100, Math.ceil(mid + 4));
}
return [lo, hi];
}
/**
* BUG-0173 — recharts' <ResponsiveContainer> is not zoom-safe.
*
* @front scales the whole UI with `body { zoom: var(--ui-scale) }` (FEAT-0315). Under
* that zoom `getBoundingClientRect()` reports ZOOMED px while the layout-box reads
* (`clientWidth`, ResizeObserver `contentRect`) report unzoomed LAYOUT px — 1108 vs
* 554 for the same element at `--ui-scale: 2`.
*
* recharts (2.15.4, `ResponsiveContainer.js:101-104`) SEEDS its size from
* `getBoundingClientRect()` and only corrects it on the first ResizeObserver tick. Only
* the WIDTH corrupts, because only the width is passed as a percent (`isPercent(width)
* ? containerWidth : width`). The chart therefore paints its first frames with a viewBox
* `uiScale`× too wide; the <svg> is `width:100%`, so preserveAspectRatio fits that
* viewBox into the real box and the whole chart is drawn at `1 / uiScale` scale, then
* SNAPS — read as "the chart changes width while it animates".
*
* Handing recharts a NUMBER makes `isPercent(width)` false, so our value is used verbatim
* and the getBoundingClientRect() path never runs. We measure in layout px, seeding in a
* layout effect so the first painted frame is already correct.
*
* @front carries the same fix as a shared <ZoomSafeChart>; this package cannot import it,
* so the measurement is inlined here.
*/
/**
* Measure layout width for recharts (BUG-0173 zoom-safe).
* `remeasureKey` forces a re-measure when a host mounts the chart inside a
* newly-opened modal (first paint often reports width 0 while opacity/flex settle).
*/
function useLayoutWidth(remeasureKey) {
const ref = require$$0.useRef(null);
const [width, setWidth] = require$$0.useState(0);
require$$0.useLayoutEffect(() => {
const el = ref.current;
if (!el)
return;
let cancelled = false;
const readWidth = () => {
const cs = getComputedStyle(el);
const inset = cs.boxSizing === 'border-box'
? parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight) +
parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth)
: 0;
let w = (parseFloat(cs.width) || 0) - inset;
if (!(w > 0))
w = el.clientWidth;
// Modal/flex first frames: walk up for a non-zero content box.
if (!(w > 0)) {
let p = el.parentElement;
for (let i = 0; i < 4 && p; i++, p = p.parentElement) {
if (p.clientWidth > 0) {
w = p.clientWidth;
break;
}
}
}
return Math.max(0, w);
};
const apply = () => {
if (cancelled)
return;
const next = readWidth();
if (next <= 0)
return;
setWidth((prev) => (Math.abs(next - prev) < 0.5 ? prev : next));
};
apply();
// Modal fade-in / tab switch: re-measure after layout settles.
const raf = requestAnimationFrame(() => requestAnimationFrame(apply));
const t0 = window.setTimeout(apply, 0);
const t1 = window.setTimeout(apply, 50);
const t2 = window.setTimeout(apply, 200);
const observer = new ResizeObserver((entries) => {
const next = entries[0]?.contentRect?.width ?? 0;
if (next <= 0) {
apply(); // fall back to parent walk
return;
}
setWidth((prev) => (Math.abs(next - prev) < 0.5 ? prev : next));
});
observer.observe(el);
if (el.parentElement)
observer.observe(el.parentElement);
return () => {
cancelled = true;
cancelAnimationFrame(raf);
window.clearTimeout(t0);
window.clearTimeout(t1);
window.clearTimeout(t2);
observer.disconnect();
};
}, [remeasureKey]);
return [ref, width];
}
// =============================================================================
// Constants
// =============================================================================
const CHART_GRID_COLORS = {
grid: 'var(--stroke, rgba(128, 128, 128, 0.15))',
axis: 'var(--stroke-2, rgba(128, 128, 128, 0.3))',
text: 'var(--ink-faint, rgba(128, 128, 128, 0.8))',
};
const MERIDIAN_CHART_COLORS = [
'var(--mds-chart-mono-1, var(--accent, #17171C))',
'var(--mds-chart-mono-2, rgba(var(--ink-rgb, 23, 23, 28), 0.58))',
'var(--mds-chart-mono-3, rgba(var(--ink-rgb, 23, 23, 28), 0.36))',
'var(--mds-chart-mono-4, rgba(var(--ink-rgb, 23, 23, 28), 0.22))',
'var(--mds-chart-status-info, var(--info, #2563eb))',
'var(--mds-chart-status-ok, var(--ok, #1a7f4b))',
'var(--mds-chart-status-warn, var(--warn, #9a6b00))',
'var(--mds-chart-status-bad, var(--bad, #b42318))',
'var(--mds-chart-status-arch, var(--arch, #8A8A92))',
'var(--mds-chart-status-gold, var(--gold, #f5a623))',
];
const PERIOD_OPTIONS = [
{ value: 'day', label: 'Day' },
{ value: 'week', label: 'Week' },
{ value: 'month', label: 'Month' },
{ value: 'year', label: 'Year' },
{ value: 'all', label: 'All' },
];
const PERIOD_MS = {
day: 24 * 60 * 60 * 1000,
week: 7 * 24 * 60 * 60 * 1000,
month: 30 * 24 * 60 * 60 * 1000,
year: 365 * 24 * 60 * 60 * 1000,
};
// =============================================================================
// Helpers
// =============================================================================
function formatNumber(n) {
if (n >= 1000000)
return `${(n / 1000000).toFixed(1)}M`;
if (n >= 1000)
return `${(n / 1000).toFixed(1)}K`;
return n.toLocaleString();
}
function getMetricColor(schema, key, index) {
return schema[key]?.color || MERIDIAN_CHART_COLORS[index % MERIDIAN_CHART_COLORS.length];
}
/**
* Status transitions that mean "we could not read metrics" — never coerce to 0.
* Includes provider-side revoke/expiry AND user-initiated disconnect (BUG-0210).
*/
const OFFLINE_EVENTS = new Set([
'token_revoked',
'token_expired',
'error',
'disconnected',
'disconnect',
]);
function isOfflineEntry(e) {
const ev = e.event != null ? String(e.event) : '';
return OFFLINE_EVENTS.has(ev);
}
/**
* Unified human label for any offline gap.
* Prefer a short "Disconnected" status; attach a specific detail when useful
* (user disconnect vs provider revoke vs expiry).
*/
function offlineReasonLabel(e) {
const ev = (e.event != null ? String(e.event) : '').toLowerCase();
const reason = (e.reason != null ? String(e.reason) : '').trim().toLowerCase();
// User-initiated disconnect (explicit disconnect flow)
if (ev === 'disconnected' ||
ev === 'disconnect' ||
reason.includes('user disconnect') ||
reason.includes('disconnected by user')) {
return 'Disconnected by user';
}
if (ev === 'token_revoked' || reason.includes('revok')) {
return 'Access revoked';
}
if (ev === 'token_expired' || reason.includes('expir')) {
return 'Connection expired';
}
if (ev === 'error') {
return 'Sync error';
}
// Generic: any period without readable metrics
return 'Disconnected';
}
function ChartTooltip({ active, payload, schema, normalize }) {
if (!active || !payload || !payload[0]?.payload)
return null;
const d = payload[0].payload;
const metricKeys = Object.keys(schema);
const groupedCount = typeof d.groupedCount === 'number' ? d.groupedCount : 1;
// BUG-0210: offline / revoked / user-disconnect periods — not zero metrics.
if (d.unavailable) {
return (jsxRuntimeExports.jsxs("div", { className: "social-chart__tooltip", children: [jsxRuntimeExports.jsx("div", { className: "social-chart__tooltip-period", children: String(d.label ?? '') }), jsxRuntimeExports.jsxs("div", { className: "social-chart__tooltip-row", style: { opacity: 0.9 }, children: [jsxRuntimeExports.jsx("span", { className: "social-chart__tooltip-dot", style: {
background: 'transparent',
border: '2px solid var(--warn, #9a6b00)',
boxSizing: 'border-box',
} }), jsxRuntimeExports.jsx("strong", { children: "Data unavailable" })] }), d.unavailableReason != null && String(d.unavailableReason) && (jsxRuntimeExports.jsx("div", { className: "social-chart__tooltip-grouped", style: { marginTop: 2 }, children: String(d.unavailableReason) })), jsxRuntimeExports.jsx("div", { className: "social-chart__tooltip-grouped", style: { marginTop: 4, fontStyle: 'italic' }, children: "Holding last known values (dashed)" })] }));
}
return (jsxRuntimeExports.jsxs("div", { className: "social-chart__tooltip", children: [jsxRuntimeExports.jsx("div", { className: "social-chart__tooltip-period", children: String(d.label ?? '') }), groupedCount > 1 && (jsxRuntimeExports.jsxs("div", { className: "social-chart__tooltip-grouped", children: [groupedCount, " syncs with same metrics"] })), metricKeys.map((key, i) => {
const rawVal = d[`raw_${key}`];
if (rawVal == null)
return null;
const pctVal = d[`${key}Pct`];
return (jsxRuntimeExports.jsxs("div", { className: "social-chart__tooltip-row", children: [jsxRuntimeExports.jsx("span", { className: "social-chart__tooltip-dot", style: { background: getMetricColor(schema, key, i) } }), schema[key].label, ": ", jsxRuntimeExports.jsx("strong", { children: formatNumber(Number(rawVal)) }), normalize && typeof pctVal === 'number' && (jsxRuntimeExports.jsxs("span", { className: "social-chart__tooltip-pct", children: ["(", pctVal.toFixed(0), "%)"] }))] }, key));
})] }));
}
// =============================================================================
// Main Component
// =============================================================================
function MetricsEvolutionChart({ entries, schema, verifiedAt, defaultPeriod = 'all', metricsStartFromZero = false, title = 'Metrics Evolution', dataTour, height = 200, normalize = true, hidePeriodPills = false, remeasureKey, }) {
const [period, setPeriod] = require$$0.useState(defaultPeriod);
// BUG-0173: measure in layout px and hand recharts a number (see useLayoutWidth).
// remeasureKey: re-run when host opens a modal / switches tabs (width often 0 on first paint).
const [chartRef, chartWidth] = useLayoutWidth(remeasureKey);
const metricKeys = require$$0.useMemo(() => Object.keys(schema), [schema]);
// Sort ascending by timestamp once (defensive — caller may pass any order)
const sortedEntries = require$$0.useMemo(() => {
return [...entries].sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
}, [entries]);
// Baseline synthesis: when only 1 real entry + verifiedAt provided, anchor a
// synthetic point at verifiedAt so chart renders on day 1.
// - metricsStartFromZero=true → all metrics = 0 (OTP-style "growth from zero")
// - metricsStartFromZero=false → all metrics = first-entry values (flat line)
const effectiveEntries = require$$0.useMemo(() => {
if (!verifiedAt || sortedEntries.length !== 1)
return sortedEntries;
const firstReal = sortedEntries[0];
const baseline = { timestamp: verifiedAt };
if (metricsStartFromZero) {
for (const key of metricKeys)
baseline[key] = 0;
}
else {
for (const key of metricKeys) {
const v = firstReal[key];
baseline[key] = typeof v === 'number' ? v : 0;
}
}
return [baseline, ...sortedEntries];
}, [sortedEntries, verifiedAt, metricsStartFromZero, metricKeys]);
// Apply period filter (window from now backward)
const filteredEntries = require$$0.useMemo(() => {
if (period === 'all')
return effectiveEntries;
const cutoff = Date.now() - PERIOD_MS[period];
return effectiveEntries.filter((e) => new Date(e.timestamp).getTime() >= cutoff);
}, [effectiveEntries, period]);
// Build chart data
const chartData = require$$0.useMemo(() => {
if (filteredEntries.length < 2 || metricKeys.length === 0)
return null;
// Detect which metrics have any numeric data in the window
// (offline lifecycle rows without metrics must not invent zeros — BUG-0210)
const activeKeys = metricKeys.filter((key) => filteredEntries.some((e) => asMetricNumber(e[key]) != null));
if (activeKeys.length === 0)
return null;
// Need at least one real metric snapshot to draw evolution. Offline-only windows
// (e.g. only token_revoked) have no series to plot.
const hasMetricSnapshot = filteredEntries.some((e) => !isOfflineEntry(e) && activeKeys.some((k) => asMetricNumber(e[k]) != null));
if (!hasMetricSnapshot)
return null;
// Find max per metric for percentage normalization (floor 1). Offline rows
// contribute nothing (no coerced zeros).
const maxVals = {};
for (const key of activeKeys) {
maxVals[key] = 1;
for (const e of filteredEntries) {
if (isOfflineEntry(e))
continue;
const v = asMetricNumber(e[key]);
if (v != null && v > maxVals[key])
maxVals[key] = v;
}
}
const fmtDate = (d) => d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
const fmtDateTime = (d) => `${fmtDate(d)} ${d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}`;
// Group consecutive identical metric snapshots. Offline events group by event type
// (never merge with a real snapshot — that would hide the gap).
const metricsKey = (e) => {
if (isOfflineEntry(e))
return `__offline__${String(e.event ?? 'unknown')}`;
return activeKeys.map((k) => {
const n = asMetricNumber(e[k]);
return n == null ? '' : n;
}).join('|');
};
const groups = [];
for (const e of filteredEntries) {
const key = metricsKey(e);
const ts = new Date(e.timestamp);
const last = groups[groups.length - 1];
if (last && metricsKey(last.entry) === key) {
last.endTs = ts;
last.count++;
}
else {
groups.push({ startTs: ts, endTs: ts, count: 1, entry: e });
}
}
// Last known Y for dashed "hold" series during offline stretches (user disconnect,
// token revoke, expiry, sync error). Solid series is null in those periods so
// reconnect does not invent a plunge to zero (BUG-0210).
const lastKnown = {};
let hasUnavailable = false;
const points = groups.map((g) => {
const e = g.entry;
const offline = isOfflineEntry(e);
const isRange = g.count > 1;
const label = isRange
? `${fmtDateTime(g.startTs)} — ${fmtDateTime(g.endTs)}`
: fmtDateTime(g.startTs);
const shortLabel = isRange
? `${fmtDate(g.startTs)} – ${fmtDate(g.endTs)}`
: fmtDate(g.startTs);
const point = { label, shortLabel, groupedCount: g.count };
if (offline) {
hasUnavailable = true;
point.unavailable = true;
point.unavailableReason = offlineReasonLabel(e);
for (const key of activeKeys) {
// Solid series: gap (null) — never coerce to 0.
point[`${key}Pct`] = null;
point[`raw_${key}`] = null;
// Dashed hold: freeze last known level across the offline window.
if (typeof lastKnown[key] === 'number') {
point[`${key}HoldPct`] = (lastKnown[key] / maxVals[key]) * 100;
point[`raw_${key}Hold`] = lastKnown[key];
}
}
return point;
}
for (const key of activeKeys) {
// Missing key on a real snapshot → null gap for that series (not 0).
const raw = asMetricNumber(e[key]);
if (raw != null)
lastKnown[key] = raw;
point[`${key}Pct`] = raw == null ? null : (raw / maxVals[key]) * 100;
point[`raw_${key}`] = raw;
// Hold stays empty on healthy points; second pass anchors the dashed start.
point[`${key}HoldPct`] = null;
point[`raw_${key}Hold`] = null;
}
return point;
});
// Anchor dashed line on the last healthy point before each offline stretch so the
// hold is continuous from "last measured" → "disconnected window".
for (let i = 0; i < points.length; i++) {
const cur = points[i];
if (!cur.unavailable || i === 0)
continue;
const prev = points[i - 1];
if (prev.unavailable)
continue; // already inside offline run
for (const key of activeKeys) {
const prevPct = prev[`${key}Pct`];
const prevRaw = prev[`raw_${key}`];
if (typeof prevPct === 'number')
prev[`${key}HoldPct`] = prevPct;
if (typeof prevRaw === 'number')
prev[`raw_${key}Hold`] = prevRaw;
}
}
const yDomain = normalize
? computeNormalizedYDomain(points, activeKeys)
: undefined;
return { points, activeKeys, hasUnavailable, yDomain };
}, [filteredEntries, metricKeys, normalize]);
// Render period pills (always visible — let user switch even when current
// window is empty so they can recover via "All").
const pills = hidePeriodPills ? null : (jsxRuntimeExports.jsx("div", { className: "metrics-chart__period-pills", role: "tablist", "aria-label": "Time period", children: PERIOD_OPTIONS.map((opt) => {
const isActive = period === opt.value;
return (jsxRuntimeExports.jsx("button", { type: "button", role: "tab", "aria-selected": isActive, className: `metrics-chart__period-pill${isActive ? ' metrics-chart__period-pill--active' : ''}`, onClick: () => setPeriod(opt.value), children: opt.label }, opt.value));
}) }));
// Empty state — pills still rendered so user can change window
if (!chartData) {
return (jsxRuntimeExports.jsxs("div", { className: "social-chart__section", ...(dataTour ? { 'data-tour': dataTour } : {}), children: [jsxRuntimeExports.jsxs("div", { className: "social-chart__header", children: [jsxRuntimeExports.jsx("span", { className: "social-chart__title", children: title }), pills] }), jsxRuntimeExports.jsx("div", { className: "social-chart__empty", children: jsxRuntimeExports.jsx("span", { style: { fontSize: '0.75rem', opacity: 0.6 }, children: filteredEntries.length < 2
? 'Not enough data for this period — try a longer window'
: 'No metrics data available' }) })] }));
}
const showDots = chartData.points.length <= 30;
const primaryKey = chartData.activeKeys[0];
const primaryColor = getMetricColor(schema, primaryKey, 0);
// Unique gradient id per render to avoid collisions when multiple charts
// are mounted on the same page (e.g., multiple providers in dashboard).
const gradientId = `mds-mec-grad-${primaryKey}-${chartData.points.length}`;
return (jsxRuntimeExports.jsxs("div", { className: "social-chart__section", ...(dataTour ? { 'data-tour': dataTour } : {}), children: [jsxRuntimeExports.jsxs("div", { className: "social-chart__header", children: [jsxRuntimeExports.jsx("span", { className: "social-chart__title", children: title }), pills] }), chartData.hasUnavailable && (jsxRuntimeExports.jsxs("div", { className: "social-chart__unavailable-hint", style: {
fontSize: '0.6875rem',
color: 'var(--ink-faint, rgba(128,128,128,0.85))',
margin: '0 0 6px',
display: 'flex',
alignItems: 'center',
gap: 6,
}, children: [jsxRuntimeExports.jsx("span", { "aria-hidden": "true", style: {
width: 18,
height: 0,
borderTop: '2px dashed var(--warn, #9a6b00)',
flex: 'none',
} }), "Dashed line = disconnected / data unavailable \u2014 holds last known values (not zero)"] })), jsxRuntimeExports.jsx("div", { ref: chartRef, style: { width: '100%', height }, children: chartWidth > 0 && (jsxRuntimeExports.jsx(recharts.ResponsiveContainer, { width: chartWidth, height: height, children: jsxRuntimeExports.jsxs(recharts.ComposedChart, { data: chartData.points, margin: { top: 12, right: 12, left: 0, bottom: 4 }, children: [jsxRuntimeExports.jsx("defs", { children: jsxRuntimeExports.jsxs("linearGradient", { id: gradientId, x1: "0", y1: "0", x2: "0", y2: "1", children: [jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: primaryColor, stopOpacity: 0.25 }), jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: primaryColor, stopOpacity: 0.02 })] }) }), jsxRuntimeExports.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: CHART_GRID_COLORS.grid, vertical: false }), jsxRuntimeExports.jsx(recharts.XAxis, { dataKey: "shortLabel", stroke: CHART_GRID_COLORS.axis, tick: { fill: CHART_GRID_COLORS.text, fontSize: 11 }, tickLine: false, axisLine: { stroke: CHART_GRID_COLORS.axis }, dy: 6, interval: "preserveStartEnd" }), jsxRuntimeExports.jsx(recharts.YAxis
// Percentage: auto-zoom to data band (see computeNormalizedYDomain).
// Absolute: fit data with recharts auto (not forced floor 0) so high-count
// series (followers ~300) still show small drops.
, {
// Percentage: auto-zoom to data band (see computeNormalizedYDomain).
// Absolute: fit data with recharts auto (not forced floor 0) so high-count
// series (followers ~300) still show small drops.
domain: normalize
? (chartData.yDomain ?? [0, 100])
: [
(dataMin) => {
if (!Number.isFinite(dataMin))
return 0;
const pad = Math.max(Math.abs(dataMin) * 0.05, 1);
return Math.max(0, Math.floor(dataMin - pad));
},
(dataMax) => {
if (!Number.isFinite(dataMax))
return 1;
const pad = Math.max(Math.abs(dataMax) * 0.05, 1);
return Math.ceil(dataMax + pad);
},
], stroke: CHART_GRID_COLORS.axis, tick: { fill: CHART_GRID_COLORS.text, fontSize: 11 }, tickLine: false, axisLine: false, width: normalize ? 36 : 40, tickCount: 6, allowDecimals: false, tickFormatter: normalize ? (v) => `${v}%` : (v) => formatNumber(v) }), jsxRuntimeExports.jsx(recharts.Tooltip, { content: jsxRuntimeExports.jsx(ChartTooltip, { schema: schema, normalize: normalize }) }), jsxRuntimeExports.jsx(recharts.Legend, { iconType: "circle", iconSize: 6, wrapperStyle: { fontSize: '11px', color: CHART_GRID_COLORS.text, paddingTop: '4px' } }), chartData.activeKeys.map((key, i) => {
const color = getMetricColor(schema, key, i);
const dataKey = normalize ? `${key}Pct` : `raw_${key}`;
const name = schema[key]?.label || key;
if (i === 0) {
return (jsxRuntimeExports.jsx(recharts.Area, { type: "monotone", dataKey: dataKey, name: name, stroke: color, strokeWidth: 2.5, fill: `url(#${gradientId})`, dot: showDots ? { fill: color, stroke: 'var(--panel, #ffffff)', strokeWidth: 2, r: 3.5 } : false, activeDot: {
fill: color,
stroke: 'var(--panel, #ffffff)',
strokeWidth: 2,
r: 5,
style: { filter: `drop-shadow(0 0 6px ${color})` },
},
// BUG-0210: do NOT bridge offline gaps (null) — zeros used to distort reconnect.
connectNulls: false }, key));
}
return (jsxRuntimeExports.jsx(recharts.Line, { type: "monotone", dataKey: dataKey, name: name, stroke: color, strokeWidth: 1.8, dot: showDots ? { fill: color, stroke: 'var(--panel, #ffffff)', strokeWidth: 1.5, r: 3 } : false, activeDot: { fill: color, stroke: 'var(--panel, #ffffff)', strokeWidth: 2, r: 5 }, connectNulls: false }, key));
}), chartData.hasUnavailable && chartData.activeKeys.map((key, i) => {
getMetricColor(schema, key, i);
const holdKey = normalize ? `${key}HoldPct` : `raw_${key}Hold`;
return (jsxRuntimeExports.jsx(recharts.Line, { type: "monotone", dataKey: holdKey, name: i === 0 ? 'Disconnected' : undefined, stroke: "var(--warn, #9a6b00)", strokeWidth: 1.6, strokeDasharray: "6 4", strokeOpacity: 0.85, legendType: i === 0 ? 'plainline' : 'none',
// Hollow-ish dots on the hold series (stable object — custom render
// callbacks have crashed recharts in some modal remount paths).
dot: {
fill: 'var(--panel, #ffffff)',
stroke: 'var(--warn, #9a6b00)',
strokeWidth: 2,
r: 3.5,
}, activeDot: {
fill: 'var(--panel, #ffffff)',
stroke: 'var(--warn, #9a6b00)',
strokeWidth: 2,
r: 5,
},
// Connect last measured → offline hold (anchor set in second pass).
connectNulls: false, isAnimationActive: false }, `hold-${key}`));
})] }) })) })] }));
}
// =============================================================================
// Constants
// =============================================================================
/**
* Derives a VerificationScore from the legacy `verified` field (0-3)
* so the card always renders the modern VerificationScoreCard style.
*/
function deriveVerificationScore(verified) {
return { score: verified, max_score: 3 };
}
// Default placeholder avatar - adapts to theme with high transparency
const getPlaceholderAvatar = (isDark) => {
const bgColor = isDark ? 'rgba(26, 26, 46, 0.15)' : 'rgba(255, 255, 255, 0.1)';
const fgColor = isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(0, 0, 0, 0.06)';
return 'data:image/svg+xml,' + encodeURIComponent(`
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="none">
<rect width="200" height="200" fill="${bgColor}"/>
<circle cx="100" cy="80" r="40" fill="${fgColor}"/>
<ellipse cx="100" cy="180" rx="60" ry="50" fill="${fgColor}"/>
</svg>
`);
};
// =============================================================================
// Icons
// =============================================================================
const Icons$1 = {
shield: (jsxRuntimeExports.jsx("svg", { viewBox: "0 0 24 24", children: jsxRuntimeExports.jsx("path", { d: "M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm-2 16l-4-4 1.41-1.41L10 14.17l6.59-6.59L18 9l-8 8z", fill: "currentColor" }) })),
};
// Social icon SVG paths — stored as strings so typeof check works cleanly
const SocialIconPaths = {
instagram: 'M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zM12 0C8.741 0 8.333.014 7.053.072 2.695.272.273 2.69.073 7.052.014 8.333 0 8.741 0 12c0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98C8.333 23.986 8.741 24 12 24c3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98C15.668.014 15.259 0 12 0zm0 5.838a6.162 6.162 0 100 12.324 6.162 6.162 0 000-12.324zM12 16a4 4 0 110-8 4 4 0 010 8zm6.406-11.845a1.44 1.44 0 100 2.881 1.44 1.44 0 000-2.881z',
threads: 'M16.017 9.386c-.095-.05-.192-.096-.29-.14a6.53 6.53 0 00-.278-1.285c-.614-1.648-1.87-2.583-3.54-2.634h-.047c-1.004 0-1.842.365-2.424 1.055l1.076.792c.396-.47.932-.55 1.348-.55h.032c.52.003.913.155 1.17.45.187.214.312.51.377.886a8.087 8.087 0 00-1.718.057c-1.606.186-2.639 1.082-2.756 2.394-.06.665.177 1.29.666 1.762.466.45 1.097.685 1.823.716.858.037 1.668-.21 2.28-.695.465-.37.79-.867.965-1.48.426.258.742.6.915 1.023.294.72.312 1.903-.626 2.842-.824.824-1.814 1.18-3.303 1.191-1.652-.013-2.902-.543-3.716-1.575-.746-.945-1.133-2.303-1.15-4.035.017-1.732.404-3.09 1.15-4.035.814-1.032 2.064-1.562 3.716-1.575 1.665.014 2.939.548 3.787 1.587.41.504.72 1.127.93 1.853l1.248-.334a7.073 7.073 0 00-1.174-2.315C14.638 2.95 13.11 2.307 11.173 2.29h-.01C9.23 2.307 7.72 2.951 6.739 4.196 5.58 5.667 4.99 7.717 4.97 10.336v.008c.02 2.62.61 4.669 1.769 6.14.981 1.245 2.491 1.889 4.424 1.906h.01c1.795-.013 3.05-.494 4.087-1.53 1.362-1.362 1.424-3.15.87-4.51a3.966 3.966 0 00-2.106-2.055c-.32-.148-.646-.27-.977-.366a4.082 4.082 0 00-.42-1.013l-.038-.063c.49-.043.96-.04 1.38.01.957.113 1.047.328 1.048.331zm-3.538 4.082c-.148.654-.565 1.233-1.448 1.233l-.112-.002c-.459-.02-.838-.166-1.096-.42-.21-.207-.32-.495-.298-.81.055-.623.543-1.035 1.35-1.128.144-.017.29-.025.437-.025.378 0 .761.04 1.136.108a3.948 3.948 0 01.031 1.044z',
facebook: 'M24 12.073c0-6.627-5.373-12-12-12s-12 5.373-12 12c0 5.99 4.388 10.954 10.125 11.854v-8.385H7.078v-3.47h3.047V9.43c0-3.007 1.792-4.669 4.533-4.669 1.312 0 2.686.235 2.686.235v2.953H15.83c-1.491 0-1.956.925-1.956 1.874v2.25h3.328l-.532 3.47h-2.796v8.385C19.612 23.027 24 18.062 24 12.073z',
x: 'M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z',
linkedin: 'M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.062 2.062 0 01-2.063-2.065 2.064 2.064 0 112.063 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z',
github: 'M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12',
google: 'M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z',
tiktok: 'M12.525.02c1.31-.02 2.61-.01 3.91-.02.08 1.53.63 3.09 1.75 4.17 1.12 1.11 2.7 1.62 4.24 1.79v4.03c-1.44-.05-2.89-.35-4.2-.97-.57-.26-1.1-.59-1.62-.93-.01 2.92.01 5.84-.02 8.75-.08 1.4-.54 2.79-1.35 3.94-1.31 1.92-3.58 3.17-5.91 3.21-1.43.08-2.86-.31-4.08-1.03-2.02-1.19-3.44-3.37-3.65-5.71-.02-.5-.03-1-.01-1.49.18-1.9 1.12-3.72 2.58-4.96 1.66-1.44 3.98-2.13 6.15-1.72.02 1.48-.04 2.96-.04 4.44-.99-.32-2.15-.23-3.02.37-.63.41-1.11 1.04-1.36 1.75-.21.51-.15 1.07-.14 1.61.24 1.64 1.82 3.02 3.5 2.87 1.12-.01 2.19-.66 2.77-1.61.19-.33.4-.67.41-1.06.1-1.79.06-3.57.07-5.36.01-4.03-.01-8.05.02-12.07z',
spotify: 'M12 0C5.4 0 0 5.4 0 12s5.4 12 12 12 12-5.4 12-12S18.66 0 12 0zm5.521 17.34c-.24.359-.66.48-1.021.24-2.82-1.74-6.36-2.101-10.561-1.141-.418.122-.779-.179-.899-.539-.12-.421.18-.78.54-.9 4.56-1.021 8.52-.6 11.64 1.32.42.18.479.659.301 1.02zm1.44-3.3c-.301.42-.841.6-1.262.3-3.239-1.98-8.159-2.58-11.939-1.38-.479.12-1.02-.12-1.14-.6-.12-.48.12-1.021.6-1.141C9.6 9.9 15 10.561 18.72 12.84c.361.181.54.78.241 1.2zm.12-3.36C15.24 8.4 8.82 8.16 5.16 9.301c-.6.179-1.2-.181-1.38-.721-.18-.601.18-1.2.72-1.381 4.26-1.26 11.28-1.02 15.721 1.621.539.3.719 1.02.419 1.56-.299.421-1.02.599-1.559.3z',
discord: 'M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189z',
telegram: 'M11.944 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0a12 12 0 00-.056 0zm4.962 7.224c.1.002.321.023.465.14a.506.506 0 01.171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.479.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z',
reddit: 'M12 0A12 12 0 000 12a12 12 0 0012 12 12 12 0 0012-12A12 12 0 0012 0zm5.01 4.744c.688 0 1.25.561 1.25 1.249a1.25 1.25 0 01-2.498.056l-2.597-.547-.8 3.747c1.824.07 3.48.632 4.674 1.488.308-.309.73-.491 1.207-.491.968 0 1.754.786 1.754 1.754 0 .716-.435 1.333-1.01 1.614a3.111 3.111 0 01.042.52c0 2.694-3.13 4.87-7.004 4.87-3.874 0-7.004-2.176-7.004-4.87 0-.183.015-.366.043-.534A1.748 1.748 0 014.028 12c0-.968.786-1.754 1.754-1.754.463 0 .898.196 1.207.49 1.207-.883 2.878-1.43 4.744-1.487l.885-4.182a.342.342 0 01.14-.197.35.35 0 01.238-.042l2.906.617a1.214 1.214 0 011.108-.701zM9.25 12C8.561 12 8 12.562 8 13.25c0 .687.561 1.248 1.25 1.248.687 0 1.248-.561 1.248-1.249 0-.688-.561-1.249-1.249-1.249zm5.5 0c-.687 0-1.248.561-1.248 1.25 0 .687.561 1.248 1.249 1.248.688 0 1.249-.561 1.249-1.249 0-.687-.562-1.249-1.25-1.249zm-5.466 3.99a.327.327 0 00-.231.094.33.33 0 000 .463c.842.842 2.484.913 2.961.913.477 0 2.105-.056 2.961-.913a.361.361 0 00.029-.463.33.33 0 00-.464 0c-.547.533-1.684.73-2.512.73-.828 0-1.979-.196-2.512-.73a.326.326 0 00-.232-.095z',
twitch: 'M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714z',
pinterest: 'M12.017 0C5.396 0 .029 5.367.029 11.987c0 5.079 3.158 9.417 7.618 11.162-.105-.949-.199-2.403.041-3.439.219-.937 1.406-5.957 1.406-5.957s-.359-.72-.359-1.781c0-1.668.967-2.914 2.171-2.914 1.023 0 1.518.769 1.518 1.69 0 1.029-.655 2.568-.994 3.995-.283 1.194.599 2.169 1.777 2.169 2.133 0 3.772-2.249 3.772-5.495 0-2.873-2.064-4.882-5.012-4.882-3.414 0-5.418 2.561-5.418 5.207 0 1.031.397 2.138.893 2.738a.36.36 0 01.083.345l-.333 1.36c-.053.22-.174.267-.402.161-1.499-.698-2.436-2.889-2.436-4.649 0-3.785 2.75-7.262 7.929-7.262 4.163 0 7.398 2.967 7.398 6.931 0 4.136-2.607 7.464-6.227 7.464-1.216 0-2.359-.631-2.75-1.378l-.748 2.853c-.271 1.043-1.002 2.35-1.492 3.146C9.57 23.812 10.763 24 12.017 24c6.624 0 11.99-5.367 11.99-11.988C24.007 5.367 18.641 0 12.017 0z',
whatsapp: 'M.057 24l1.687-6.163c-1.041-1.804-1.588-3.849-1.587-5.946.003-6.556 5.338-11.891 11.893-11.891 3.181.001 6.167 1.24 8.413 3.488 2.245 2.248 3.481 5.236 3.48 8.414-.003 6.557-5.338 11.892-11.893 11.892-1.99-.001-3.951-.5-5.688-1.448L.057 24zm6.597-3.807c1.676.995 3.276 1.591 5.392 1.592 5.448 0 9.886-4.434 9.889-9.885.002-5.462-4.415-9.89-9.881-9.892-5.452 0-9.887 4.434-9.889 9.884-.001 2.225.651 3.891 1.746 5.634l-.999 3.648 3.742-.981zm11.387-5.464c-.074-.124-.272-.198-.57-.347-.297-.149-1.758-.868-2.031-.967-.272-.099-.47-.149-.669.149-.198.297-.768.967-.941 1.165-.173.198-.347.223-.644.074-.297-.149-1.255-.462-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.297-.347.446-.521.151-.172.2-.296.3-.495.099-.198.05-.372-.025-.521-.075-.148-.669-1.611-.916-2.206-.242-.579-.487-.501-.669-.51l-.57-.01c-.198 0-.52.074-.792.372s-1.04 1.016-1.04 2.479 1.065 2.876 1.213 3.074c.149.198 2.095 3.2 5.076 4.487.709.306 1.263.489 1.694.626.712.226 1.36.194 1.872.118.571-.085 1.758-.719 2.006-1.413.248-.695.248-1.29.173-1.414z',
website: 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z',
};
/** Format metric value — shows lock icon for 0 (restricted by provider privacy) */
function formatMetricValue(value) {
if (typeof value === 'number') {
return value === 0 ? '' : value.toLocaleString();
}
return value;
}
function isMetricRestricted(value) {
return typeof value === 'number' && value === 0;
}
const RestrictedIcon = () => (jsxRuntimeExports.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", style: { opacity: 0.35 }, children: [jsxRuntimeExports.jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2", ry: "2" }), jsxRuntimeExports.jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }));
function SocialLink({ href, title, icon, metrics, avatarUrl, showChart, onFetchHistory, metricsSchema, verifiedAt }) {
const [open, setOpen] = require$$0.useState(false);
const [avatarFailed, setAvatarFailed] = require$$0.useState(false);
const [historyData, setHistoryData] = require$$0.useState(null);
const [historyLoading, setHistoryLoading] = require$$0.useState(false);
const [historyError, setHistoryError] = require$$0.useState(null);
const pathData = SocialIconPaths[icon] || SocialIconPaths['website'];
// Reset failure flag when the URL changes
require$$0.useEffect(() => { setAvatarFailed(false); }, [avatarUrl]);
// FEAT-151 Phase 2.2: fetch history when modal opens AND chart is public
require$$0.useEffect(() => {
if (!open || !showChart || !onFetchHistory || historyData !== null)
return;
let cancelled = false;
setHistoryLoading(true);
setHistoryError(null);
onFetchHistory()
.then((entries) => {
if (cancelled)
return;
setHistoryData(entries || []);
})
.catch((e) => {
if (cancelled)
return;
setHistoryError(e instanceof Error ? e.message : 'Failed to load history');
})
.finally(() => { if (!cancelled)
setHistoryLoading(false); });
return () => { cancelled = true; };
}, [open, showChart, onFetchHistory, historyData]);
// Listen for tour event to close this modal
require$$0.useEffect(() => {
const handler = () => setOpen(false);
window.addEventListener('tour:close-social-modal', handler);
return () => window.removeEventListener('tour:close-social-modal', handler);
}, []);
const hasMetrics = metrics && metrics.length > 0;
const hasUrl = href && href !== '#';
const handleOpen = () => {
setOpen(false);
if (hasUrl)
window.open(href, '_blank', 'noopener,noreferrer');
};
// Extract specific metrics for grid layout
const followers = metrics?.find(m => m.label.toLowerCase().includes('follower'));
const following = metrics?.find(m => m.label.toLowerCase().includes('following'));
const posts = metrics?.find(m => m.label.toLowerCase().includes('post') || m.label.toLowerCase().includes('media'));
const friends = metrics?.find(m => m.label.toLowerCase().includes('friend'));
const gridMetrics = [followers, following, posts, friends].filter(Boolean);
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-link", "data-provider": icon, onClick: (e) => {
e.stopPropagation();
setOpen(true);
if (icon && typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent(`tour:${icon}-modal-opened`));
}
}, ...(icon ? { 'data-tour': `profile-${icon}-badge` } : {}), children: jsxRuntimeExports.jsx("svg", { viewBox: "0 0 24 24", children: jsxRuntimeExports.jsx("path", { d: pathData, fill: "currentColor" }) }) }), open && typeof document !== 'undefined' && reactDom.createPortal(jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__social-modal", onClick: () => setOpen(false), role: "dialog", "aria-modal": "true", "aria-label": title, children: [jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-modal-backdrop" }), jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__social-modal-content", onClick: (e) => e.stopPropagation(), children: [jsxRuntimeExports.jsx("button", { className: "mds-profile-card__social-modal-close-x", onClick: () => setOpen(false), "aria-label": "Close", type: "button", children: jsxRuntimeExports.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntimeExports.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) }), jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__social-modal-header", children: [jsxRuntimeExports.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { d: pathData }) }), jsxRuntimeExports.jsx("span", { children: title })] }), jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-modal-avatar-wrap", children: avatarUrl && !avatarFailed ? (jsxRuntimeExports.jsx("img", { src: avatarUrl, alt: title, className: "mds-profile-card__social-modal-avatar", referrerPolicy: "no-referrer", onError: () => setAvatarFailed(true) })) : (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-modal-avatar mds-profile-card__social-modal-avatar--placeholder", children: jsxRuntimeExports.jsx("svg", { width: "36", height: "36", viewBox: "0 0 24 24", fill: "currentColor", style: { opacity: 0.3 }, children: jsxRuntimeExports.jsx("path", { d: pathData }) }) })) }), hasMetrics && gridMetrics.length > 0 ? (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-modal-grid", children: gridMetrics.map((m) => (jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__social-modal-kpi", children: [jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-modal-kpi-value", children: isMetricRestricted(m.value) ? jsxRuntimeExports.jsx(RestrictedIcon, {}) : formatMetricValue(m.value) }), jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-modal-kpi-label", children: m.label })] }, m.label))) })) : hasMetrics ? (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-modal-metrics", children: metrics.map((m, i) => (jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__social-modal-row", children: [jsxRuntimeExports.jsx("span", { className: "mds-profile-card__social-modal-label", children: m.label }), jsxRuntimeExports.jsx("span", { className: "mds-profile-card__social-modal-value", children: isMetricRestricted(m.value) ? jsxRuntimeExports.jsx(RestrictedIcon, {}) : formatMetricValue(m.value) })] }, i))) })) : null, showChart && (jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__social-modal-chart", children: [historyLoading && (jsxRuntimeExports.jsx("div", { style: { padding: '20px', textAlign: 'center', opacity: 0.7, fontSize: '13px' }, children: "Loading history\u2026" })), historyError && (jsxRuntimeExports.jsx("div", { style: { padding: '12px', textAlign: 'center', color: '#ff8080', fontSize: '12px' }, children: historyError })), historyData && !historyLoading && !historyError && (jsxRuntimeExports.jsx(MetricsEvolutionChart, { entries: historyData, schema: metricsSchema || {}, verifiedAt: verifiedAt, metricsStartFromZero: false }))] })), hasUrl && jsxRuntimeExports.jsxs("button", { className: "mds-profile-card__social-modal-open", onClick: handleOpen, children: [jsxRuntimeExports.jsxs("span", { children: ["Open ", title] }), jsxRuntimeExports.jsxs("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntimeExports.jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }), jsxRuntimeExports.jsx("polyline", { points: "15 3 21 3 21 9" }), jsxRuntimeExports.jsx("line", { x1: "10", y1: "14", x2: "21", y2: "3" })] })] })] })] }), document.body)] }));
}
function UsernameLinks({ username, className }) {
const segments = username.split('.');
// Detect mode from first segment length (5 segments only)
// 2 chars = geo-first, 3 chars = area-first, 4+ chars = username-first
const firstLen = segments.length > 0 ? segments[0].length : 0;
const isUsernameFirst = segments.length === 5 && firstLen >= 4;
// Find the name segment index:
// geo-first/area-first: last segment (index 4)
// username-first: first segment (index 0)
const nameIndex = isUsernameFirst ? 0 : segments.length - 1;
const nameSegment = segments[nameIndex];
return (jsxRuntimeExports.jsx("span", { className: className, children: segments.map((segment, index) => {
let path;
if (index === nameIndex) {
// Name segment always links to /{name} (username-first entry point)
path = '/' + nameSegment;
}
else {
// Progressive path from the segments as displayed
path = '/' + segments.slice(0, index + 1).join('.');
}
const isLast = index === segments.length - 1;
return (jsxRuntimeExports.jsxs(require$$0.Fragment, { children: [jsxRuntimeExports.jsx("a", { href: path, className: "mds-profile-card__username-link", title: path, children: segment }), !isLast && jsxRuntimeExports.jsx("span", { className: "mds-profile-card__username-separator", children: "." })] }, index));
}) }));
}
// =============================================================================
// Main Component
// =============================================================================
function ProfileCard({ user, variant = 'full', photoWidth, showSocial = false, socialLinks, socialMetrics, socialAvatars, socialChartVisible, onFetchSocialHistory, socialMetricsSchema, socialVerifiedAt, showRotatingInfo = false, photoSlot, className = '', isPrivate = false, hidePhoto = false, hideVerificationScore = false, onShieldClick, }) {
const [showPhotoModal, setShowPhotoModal] = require$$0.useState(false);
const [showScoreModal, setShowScoreModal] = require$$0.useState(false);
// Listen for tour event to close score modal
require$$0.useEffect(() => {
const handler = () => setShowScoreModal(false);
window.addEventListener('tour:close-score-modal', handler);
return () => window.removeEventListener('tour:close-score-modal', handler);
}, []);
const [isDarkTheme, setIsDarkTheme] = require$$0.useState(true);
const [isPhotoLoading, setIsPhotoLoading] = require$$0.useState(true);
const [loadingProgress, setLoadingProgress] = require$$0.useState(0);
// Tracks image load failure — hides photo section when a stale/broken URL
// (e.g., user removed their photo but a cached URL lingers) fails to load,
// so the card renders as "no photo" instead of showing the browser's broken
// image placeholder.
const [photoLoadError, setPhotoLoadError] = require$$0.useState(false);
// Reset error flag whenever the photo URL changes so a new URL gets a fresh try
require$$0.useEffect(() => {
setPhotoLoadError(false);
}, [user.photo_url_medium, user.photo_url_full]);
const nameRef = require$$0.useRef(null);
const progressIntervalRef = require$$0.useRef(null);
// Calculate photo width based on variant
const computedPhotoWidth = photoWidth ?? (variant === 'full' ? 280 : 200);
// Resolve verification score: use provided data or derive from legacy `verified` field
const verificationScore = user.verification_score ?? deriveVerificationScore(user.verified);
// Detect theme
require$$0.useEffect(() => {
const checkTheme = () => {
const theme = document.documentElement.getAttribute('data-theme');
setIsDarkTheme(theme !== 'light');
};
checkTheme();
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.attributeName === 'data-theme') {
checkTheme();
}
});
});
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });
return () => observer.disconnect();
}, []);
// Auto-shrink name to fit container without line breaks
const shrinkName = require$$0.useCallback(() => {
const el = nameRef.current;
if (!el)
return;
// Reset to CSS base size before measuring
el.style.fontSize = '';
// Force reflow so measurements reflect the reset
const baseFontSize = parseFloat(getComputedStyle(el).fontSize);
const minFontSize = 16;
let size = baseFontSize;
while (el.scrollWidth > el.clientWidth && size > minFontSize) {
size -= 1;
el.style.fontSize = `${size}px`;
}
}, []);
// Run shrink synchronously after DOM update
require$$0.useLayoutEffect(() => {
shrinkName();
}, [user.full_name, user.username, isPrivate, shrinkName]);
// Re-run after fonts finish loading (OCR-B may load after first paint)
require$$0.useEffect(() => {
document.fonts.ready.then(shrinkName);
}, [shrinkName]);
// Photo loading progress animation
require$$0.useEffect(() => {
const hasPhoto = !!(user.photo_url_medium || user.photo_url_full);
if (!hasPhoto || isPrivate || hidePhoto) {
setIsPhotoLoading(false);
setLoadingProgress(100);
return;
}
// Start progress animation
setIsPhotoLoading(true);
setLoadingProgress(10);
progressIntervalRef.current = setInterval(() => {
setLoadingProgress(prev => {
if (prev >= 90)
return prev;
const increment = prev < 50 ? 8 : prev < 70 ? 4 : 2;
return Math.min(prev + increment, 90);
});
}, 150);
return () => {
if (progressIntervalRef.current) {
clearInterval(progressIntervalRef.current);
}
};
}, [user.photo_url_medium, user.photo_url_full, isPrivate, hidePhoto]);
const handlePhotoLoad = require$$0.useCallback(() => {
if (progressIntervalRef.current) {
clearInterval(progressIntervalRef.current);
}
setLoadingProgress(100);
// Small delay before hiding progress bar for smooth transition
setTimeout(() => {
setIsPhotoLoading(false);
}, 300);
}, []);
const handlePhotoError = require$$0.useCallback(() => {
if (progressIntervalRef.current) {
clearInterval(progressIntervalRef.current);
}
setPhotoLoadError(true);
setIsPhotoLoading(false);
setLoadingProgress(100);
}, []);
// Get display values
const displayName = user.full_name || user.username || 'User';
// Always show area_name (full name like "Developer") instead of code (like "DEV")
const profession = user.area_name || '';
const getLocation = () => {
if (!user.city_code && !user.state_code && !user.country_code)
return null;
const city = user.city_name || user.city_code?.toUpperCase() || '';
const state = user.state_code?.toUpperCase() || '';
const country = user.country_name || user.country_code?.toUpperCase() || '';
const parts = [city, state, country].filter(Boolean);
return parts.join(', ');
};
const createdDate = new Date(user.created_at);
const memberSince = createdDate.getFullYear().toString();
const memberSinceFull = `${createdDate.toLocaleString('en', { month: 'long' })}, ${createdDate.getFullYear()}`;
const photoUrl = user.photo_url_medium || user.photo_url_full || getPlaceholderAvatar(isDarkTheme);
// Full size photo for modal (prioritize full, then medium)
const photoUrlFull = user.photo_url_full || user.photo_url_medium;
const hasRealPhoto = !!(user.photo_url_medium || user.photo_url_full);
// Determine if photo should be hidden (private OR hidePhoto prop OR load error)
const shouldHidePhoto = isPrivate || hidePhoto || photoLoadError;
return (jsxRuntimeExports.jsxs("article", { className: `mds-profile-card ${shouldHidePhoto ? 'mds-profile-card--private' : ''} ${className}`, style: { '--mds-profile-card-photo-width': `${computedPhotoWidth}px` }, onMouseEnter: () => document.dispatchEvent(new CustomEvent('mds-profile-card-hover')), children: [jsxRuntimeExports.jsx("div", { className: "mds-profile-card__grain" }), isPhotoLoading && hasRealPhoto && !shouldHidePhoto && !photoSlot && (jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__progress-bar", children: [jsxRuntimeExports.jsx("div", { className: "mds-profile-card__progress-fill", style: { width: `${loadingProgress}%` } }), jsxRuntimeExports.jsx("div", { className: "mds-profile-card__progress-glow", style: { left: `${loadingProgress}%` } })] })), !shouldHidePhoto && (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__photo", children: photoSlot ? (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__photo-slot", children: photoSlot })) : (jsxRuntimeExports.jsx("img", { src: photoUrl, alt: displayName, className: `mds-profile-card__avatar ${hasRealPhoto ? 'mds-profile-card__avatar--clickable' : ''}`, onClick: hasRealPhoto ? () => setShowPhotoModal(true) : undefined, role: hasRealPhoto ? 'button' : undefined, tabIndex: hasRealPhoto ? 0 : undefined, onKeyDown: hasRealPhoto ? (e) => e.key === 'Enter' && setShowPhotoModal(true) : undefined, onLoad: handlePhotoLoad, onError: hasRealPhoto ? handlePhotoError : undefined, style: isPhotoLoading && hasRealPhoto ? { opacity: 0 } : undefined })) })), showPhotoModal && photoUrlFull && typeof document !== 'undefined' && reactDom.createPortal(jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__photo-modal", onClick: () => setShowPhotoModal(false), role: "dialog", "aria-modal": "true", "aria-label": "Enlarged photo - click anywhere to close", children: [jsxRuntimeExports.jsx("div", { className: "mds-profile-card__photo-modal-backdrop" }), jsxRuntimeExports.jsx("div", { className: "mds-profile-card__photo-modal-content", children: jsxRuntimeExports.jsx("img", { src: photoUrlFull, alt: displayName, className: "mds-profile-card__photo-modal-image" }) })] }), document.body), jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__content", children: [jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__identity", children: [jsxRuntimeExports.jsx("h1", { ref: nameRef, className: "mds-profile-card__name", children: isPrivate ? 'PRIVATE ACCOUNT' : displayName.toUpperCase() }), variant === 'full' && profession && (jsxRuntimeExports.jsx("p", { className: "mds-profile-card__profession", children: user.area_code ? (jsxRuntimeExports.jsx("a", { href: `/${user.area_code}`, className: "mds-profile-card__nav-link", children: profession.toUpperCase() })) : profession.toUpperCase() })), !isPrivate && variant === 'compact' && (jsxRuntimeExports.jsx(UsernameLinks, { username: user.username, className: "mds-profile-card__username" }))] }), jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__details", children: [variant === 'full' && getLocation() && (jsxRuntimeExports.jsx("p", { className: "mds-profile-card__location", children: user.city_code && user.country_code && user.state_code ? (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("a", { href: `/${user.country_code}.${user.state_code}.${user.city_code}`, className: "mds-profile-card__nav-link", children: user.city_name || user.city_code.toUpperCase() }), ', ', jsxRuntimeExports.jsx("a", { href: `/${user.country_code}.${user.state_code}`, className: "mds-profile-card__nav-link", children: user.state_code.toUpperCase() }), ', ', jsxRuntimeExports.jsx("a", { href: `/${user.country_code}`, className: "mds-profile-card__nav-link", children: user.country_name || user.country_code.toUpperCase() })] })) : getLocation() })), variant === 'full' && (jsxRuntimeExports.jsx(UsernameLinks, { username: user.username, className: "mds-profile-card__username-detail" })), !isPrivate && variant === 'compact' && user.email && (jsxRuntimeExports.jsx("p", { className: "mds-profile-card__email", children: user.email })), !isPrivate && variant === 'compact' && (jsxRuntimeExports.jsxs("span", { className: "mds-profile-card__member", children: ["Member since ", memberSince] }))] }), !isPrivate && variant === 'compact' && (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__verification", children: jsxRuntimeExports.jsxs("span", { className: "mds-profile-card__verified-badge", title: `Verification Score: ${verificationScore.score}`, children: [Icons$1.shield, jsxRuntimeExports.jsx("span", { children: verificationScore.score })] }) })), !isPrivate && showSocial && socialLinks && (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__social-icons", children: Object.entries(socialLinks).map(([provider, url]) => (jsxRuntimeExports.jsx(SocialLink, { href: url || '#', title: provider.charAt(0).toUpperCase() + provider.slice(1), icon: provider, metrics: socialMetrics?.[provider], avatarUrl: socialAvatars?.[provider], showChart: socialChartVisible?.[provider], onFetchHistory: onFetchSocialHistory ? () => onFetchSocialHistory(provider) : undefined, metricsSchema: socialMetricsSchema?.[provider], verifiedAt: socialVerifiedAt?.[provider] }, provider))) }))] }), !isPrivate && !hideVerificationScore && variant === 'full' && (jsxRuntimeExports.jsx("button", { className: "mds-profile-card__corner-badge mds-profile-card__corner-badge--shield", onClick: (e) => {
if (onShieldClick) {
onShieldClick(e);
}
else {
setShowScoreModal(true);
}
if (typeof window !== 'undefined') {
window.dispatchEvent(new CustomEvent('tour:shield-modal-opened'));
}
}, title: "Learn about Verification Score", "data-tour": "profile-shield-badge", children: jsxRuntimeExports.jsx(VerificationShield, { score: verificationScore.score, size: "xl" }) })), !isPrivate && variant === 'full' && (jsxRuntimeExports.jsx("div", { className: "mds-profile-card__corner-meta", children: jsxRuntimeExports.jsxs("span", { children: [user.registration_sequence ? `User #${user.registration_sequence.toLocaleString()} — ` : '', "Since ", memberSinceFull] }) })), showScoreModal && typeof document !== 'undefined' && reactDom.createPortal(jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__score-modal", onClick: () => setShowScoreModal(false), role: "dialog", "aria-modal": "true", "aria-label": "Verification Score", children: [jsxRuntimeExports.jsx("div", { className: "mds-profile-card__score-modal-backdrop" }), jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__score-modal-content", onClick: (e) => e.stopPropagation(), children: [jsxRuntimeExports.jsxs("div", { className: "mds-profile-card__score-modal-header", children: [Icons$1.shield, jsxRuntimeExports.jsx("span", { children: "Verification Score" })] }), jsxRuntimeExports.jsx("p", { className: "mds-profile-card__score-modal-text", children: "The Verification Score reflects how thoroughly an account has been verified on Metropolle. It is calculated based on multiple independent trust signals \u2014 such as email confirmation, identity documents, two-factor authentication, phone verification, and community referrals \u2014 each contributing one point to the overall score." }), jsxRuntimeExports.jsx("p", { className: "mds-profile-card__score-modal-text", children: "A higher score indicates a stronger level of trust and authenticity. The specific criteria that make up the score are kept private to protect user security, but the final number provides a reliable measure of account credibility within the platform." }), jsxRuntimeExports.jsx("button", { className: "mds-profile-card__score-modal-close", onClick: () => setShowScoreModal(false), children: "Close" })] })] }), document.body)] }));
}
const variantElementMap = {
h1: 'h1',
h2: 'h2',
h3: 'h3',
h4: 'h4',
body: 'p',
caption: 'span',
brand: 'h1'
};
const variantSizeMap = {
h1: '3xl',
h2: '2xl',
h3: 'xl',
h4: 'lg',
body: 'base',
caption: 'sm',
brand: '4xl'
};
/**
* Typography Component
*
* Sistema de tipografia baseado nos design tokens da Metropolle.
* Implementa a identidade tipográfica Helvetica com letter-spacing característico.
*/
const Typography = require$$0.forwardRef(({ variant = 'body', size, as, children, className, color = 'primary', weight = 'normal', ...props }, ref) => {
const Component = (as || variantElementMap[variant]);
const finalSize = size || variantSizeMap[variant];
return (jsxRuntimeExports.jsx(Component, { ref: ref, className: cn('mds-text', `mds-text--${variant}`, `mds-text--size-${finalSize}`, `mds-text--color-${color}`, `mds-text--weight-${weight}`, className), ...props, children: children }));
});
Typography.displayName = 'Typography';
const BrandLogo = require$$0.forwardRef(({ size = 'md', weight = 'bold', // Peso bold por padrão para a marca
className, children = 'Metropolle', ...props }, ref) => {
const sizeMap = {
sm: '2xl',
md: '4xl',
lg: '4xl', // Same as md but with different mobile behavior
};
return (jsxRuntimeExports.jsx(Typography, { ref: ref, variant: "brand", size: sizeMap[size], weight: weight, className: cn('mds-brand-logo', `mds-brand-logo--${size}`, className), ...props, children: children }));
});
BrandLogo.displayName = 'BrandLogo';
/**
* Button Component
*
* Componente de botão com variantes baseadas no design system Metropolle.
* Inclui suporte a glass morphism e estados de loading.
*/
const Button = require$$0.forwardRef(({ variant = 'primary', size = 'md', loading = false, leftIcon, rightIcon, fullWidth = false, className, children, disabled, ...props }, ref) => {
const isDisabled = disabled || loading;
return (jsxRuntimeExports.jsxs("button", { ref: ref, className: cn(
// Base classes
'mds-button', `mds-button--${variant}`, `mds-button--${size}`,
// State classes
fullWidth && 'mds-button--full-width', loading && 'mds-button--loading', isDisabled && 'mds-button--disabled', className), disabled: isDisabled, ...props, children: [leftIcon && !loading && (jsxRuntimeExports.jsx("span", { className: "mds-button__icon mds-button__icon--left", children: leftIcon })), loading && (jsxRuntimeExports.jsx("span", { className: "mds-button__icon mds-button__icon--left", children: jsxRuntimeExports.jsx(LoadingSpinner, {}) })), jsxRuntimeExports.jsx("span", { className: "mds-button__content", children: children }), rightIcon && !loading && (jsxRuntimeExports.jsx("span", { className: "mds-button__icon mds-button__icon--right", children: rightIcon }))] }));
});
Button.displayName = 'Button';
/**
* Loading Spinner Component
*/
const LoadingSpinner = () => (jsxRuntimeExports.jsxs("svg", { className: "mds-spinner", width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: [jsxRuntimeExports.jsx("circle", { className: "mds-spinner__track", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none", opacity: "0.25" }), jsxRuntimeExports.jsx("circle", { className: "mds-spinner__path", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", fill: "none", strokeDasharray: "40 20", strokeLinecap: "round" })] }));
/**
* FEAT-0315 zoom-safety for the portalled dropdown.
*
* @front scales the whole UI with `body { zoom: var(--ui-scale) }` (medium=1.5,
* large=2). The Select dropdown portals into that zoomed <body>, so its fixed
* `top/left/width` are interpreted in PRE-zoom CSS px — while
* `getBoundingClientRect()` and `window.innerHeight` report POST-zoom visual px.
* Using the raw rect would land the dropdown at coords × scale (shifted
* down-right). Every rect/viewport value must be divided by the scale first.
*
* The factor is read from the `--ui-scale` custom property (set by @front's
* globals.css; stays 1 where `zoom` is unsupported thanks to the @supports
* gate). When the property is absent (another consumer with its own zoom), fall
* back to the rendered-vs-layout width ratio of the trigger:
* rect.width is zoomed, offsetWidth is not.
*/
function resolveBodyZoom(cssVarValue, rectWidth, layoutWidth) {
const fromVar = parseFloat(cssVarValue);
if (Number.isFinite(fromVar) && fromVar > 0)
return fromVar;
if (layoutWidth > 0) {
const derived = rectWidth / layoutWidth;
if (Number.isFinite(derived) && derived > 0)
return derived;
}
return 1;
}
/**
* Select Component (Design System)
*
* Custom dropdown select that renders consistently across all browsers.
* Unlike native <select>, this component renders the dropdown via JavaScript,
* ensuring proper theming support on Edge/Chrome Windows.
*
* @example
* ```tsx
* <Select
* options={[
* { label: 'Option 1', value: '1' },
* { label: 'Option 2', value: '2' },
* ]}
* value={selectedValue}
* onChange={setSelectedValue}
* placeholder="Select an option..."
* />
* ```
*/
const Select = require$$0.forwardRef(({ options, value, onChange, placeholder = 'Select...', variant = 'themed', size = 'md', disabled = false, loading = false, error = false, className, dropdownClassName, id, name, 'aria-label': ariaLabel, fullWidth = false, searchable = false, searchPlaceholder = 'Search...', maxHeight = 300, zIndex = 1050, }, ref) => {
const [isOpen, setIsOpen] = require$$0.useState(false);
const [searchTerm, setSearchTerm] = require$$0.useState('');
const [highlightedIndex, setHighlightedIndex] = require$$0.useState(-1);
const [mounted, setMounted] = require$$0.useState(false);
const triggerRef = require$$0.useRef(null);
const dropdownRef = require$$0.useRef(null);
const searchInputRef = require$$0.useRef(null);
const listRef = require$$0.useRef(null);
// Combine refs
const combinedRef = (el) => {
triggerRef.current = el;
if (typeof ref === 'function') {
ref(el);
}
else if (ref) {
ref.current = el;
}
};
// Client-side only
require$$0.useEffect(() => {
setMounted(true);
}, []);
// Filter options based on search
const filteredOptions = require$$0.useMemo(() => {
if (!searchTerm)
return options;
const term = searchTerm.toLowerCase();
return options.filter(opt => {
const label = typeof opt.label === 'string' ? opt.label : String(opt.value);
return label.toLowerCase().includes(term);
});
}, [options, searchTerm]);
// Get selected option label
const selectedOption = require$$0.useMemo(() => {
return options.find(opt => opt.value === value);
}, [options, value]);
// Handle dropdown positioning
const [dropdownPosition, setDropdownPosition] = require$$0.useState({ top: 0, left: 0, width: 0 });
const updateDropdownPosition = require$$0.useCallback(() => {
if (!triggerRef.current)
return;
const rect = triggerRef.current.getBoundingClientRect();
// FEAT-0315: @front applies `body { zoom: var(--ui-scale) }`. The dropdown portals
// into the zoomed <body>, so fixed coords are PRE-zoom CSS px while
// getBoundingClientRect()/innerHeight report POST-zoom visual px — divide by the
// scale or the dropdown lands at coords × scale (see uiScale.ts).
const scale = resolveBodyZoom(getComputedStyle(document.documentElement).getPropertyValue('--ui-scale'), rect.width, triggerRef.current.offsetWidth);
const cssTop = rect.top / scale;
const cssBottom = rect.bottom / scale;
const cssLeft = rect.left / scale;
const cssWidth = rect.width / scale;
const viewportHeight = window.innerHeight / scale;
const spaceBelow = viewportHeight - cssBottom;
const spaceAbove = cssTop;
// Determine if dropdown should open above or below
const dropdownHeight = Math.min(maxHeight, filteredOptions.length * 40 + (searchable ? 48 : 0));
const openAbove = spaceBelow < dropdownHeight && spaceAbove > spaceBelow;
setDropdownPosition({
top: openAbove ? cssTop - dropdownHeight : cssBottom + 4,
left: cssLeft,
width: cssWidth,
});
}, [maxHeight, filteredOptions.length, searchable]);
// Open dropdown
const openDropdown = require$$0.useCallback(() => {
if (disabled || loading)
return;
updateDropdownPosition();
setIsOpen(true);
setSearchTerm('');
setHighlightedIndex(value ? filteredOptions.findIndex(opt => opt.value === value) : 0);
}, [disabled, loading, updateDropdownPosition, value, filteredOptions]);
// Close dropdown
const closeDropdown = require$$0.useCallback(() => {
setIsOpen(false);
setSearchTerm('');
setHighlightedIndex(-1);
triggerRef.current?.focus();
}, []);
// Handle option select
const handleSelect = require$$0.useCallback((optionValue) => {
// Safety check: ensure we're passing a string, not an object
const safeValue = typeof optionValue === 'string' ? optionValue : String(optionValue);
onChange?.(safeValue);
closeDropdown();
}, [onChange, closeDropdown]);
// Keyboard navigation
const handleKeyDown = require$$0.useCallback((e) => {
if (disabled || loading)
return;
switch (e.key) {
case 'Enter':
case ' ':
e.preventDefault();
if (isOpen && highlightedIndex >= 0 && filteredOptions[highlightedIndex]) {
const opt = filteredOptions[highlightedIndex];
if (!opt.disabled) {
handleSelect(opt.value);
}
}
else if (!isOpen) {
openDropdown();
}
break;
case 'ArrowDown':
e.preventDefault();
if (!isOpen) {
openDropdown();
}
else {
setHighlightedIndex(prev => {
const next = prev + 1;
return next >= filteredOptions.length ? 0 : next;
});
}
break;
case 'ArrowUp':
e.preventDefault();
if (isOpen) {
setHighlightedIndex(prev => {
const next = prev - 1;
return next < 0 ? filteredOptions.length - 1 : next;
});
}
break;
case 'Escape':
e.preventDefault();
closeDropdown();
break;
case 'Tab':
if (isOpen) {
closeDropdown();
}
break;
case 'Home':
if (isOpen) {
e.preventDefault();
setHighlightedIndex(0);
}
break;
case 'End':
if (isOpen) {
e.preventDefault();
setHighlightedIndex(filteredOptions.length - 1);
}
break;
}
}, [disabled, loading, isOpen, highlightedIndex, filteredOptions, handleSelect, openDropdown, closeDropdown]);
// Click outside to close
require$$0.useEffect(() => {
if (!isOpen)
return;
const handleClickOutside = (e) => {
if (triggerRef.current?.contains(e.target) ||
dropdownRef.current?.contains(e.target)) {
return;
}
closeDropdown();
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, [isOpen, closeDropdown]);
// Update position on scroll/resize
require$$0.useEffect(() => {
if (!isOpen)
return;
const handleUpdate = () => updateDropdownPosition();
window.addEventListener('scroll', handleUpdate, true);
window.addEventListener('resize', handleUpdate);
return () => {
window.removeEventListener('scroll', handleUpdate, true);
window.removeEventListener('resize', handleUpdate);
};
}, [isOpen, updateDropdownPosition]);
// Focus search input when dropdown opens
require$$0.useEffect(() => {
if (isOpen && searchable && searchInputRef.current) {
searchInputRef.current.focus();
}
}, [isOpen, searchable]);
// Scroll highlighted option into view
require$$0.useEffect(() => {
if (!isOpen || highlightedIndex < 0 || !listRef.current)
return;
const highlighted = listRef.current.children[highlightedIndex];
if (highlighted) {
highlighted.scrollIntoView({ block: 'nearest' });
}
}, [isOpen, highlightedIndex]);
// Size classes
const sizeClasses = {
sm: 'mds-select--sm',
md: 'mds-select--md',
lg: 'mds-select--lg',
};
// Variant classes
const variantClasses = {
base: 'mds-select--base',
themed: 'mds-select--themed',
dashboard: 'mds-select--themed',
};
const triggerClasses = cn('mds-select-trigger', sizeClasses[size], variantClasses[variant], isOpen && 'mds-select-trigger--open', disabled && 'mds-select-trigger--disabled', loading && 'mds-select-trigger--loading', error && 'mds-select-trigger--error', fullWidth && 'mds-select-trigger--full-width', className);
const dropdownClasses = cn('mds-select-dropdown', variantClasses[variant], dropdownClassName);
// Hidden input for form submission
const hiddenInput = name ? (jsxRuntimeExports.jsx("input", { type: "hidden", name: name, value: value || '' })) : null;
// Dropdown portal content
const dropdownContent = isOpen && mounted ? reactDom.createPortal(jsxRuntimeExports.jsxs("div", { ref: dropdownRef, className: dropdownClasses, style: {
position: 'fixed',
top: dropdownPosition.top,
left: dropdownPosition.left,
width: dropdownPosition.width,
maxHeight,
zIndex,
}, role: "listbox", "aria-label": ariaLabel || placeholder, children: [searchable && (jsxRuntimeExports.jsxs("div", { className: "mds-select-search", children: [jsxRuntimeExports.jsx("input", { ref: searchInputRef, type: "text", className: "mds-select-search__input", placeholder: searchPlaceholder, value: searchTerm, onChange: (e) => {
setSearchTerm(e.target.value);
setHighlightedIndex(0);
}, onKeyDown: handleKeyDown, "aria-label": "Search options" }), jsxRuntimeExports.jsx("svg", { className: "mds-select-search__icon", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z", clipRule: "evenodd" }) })] })), jsxRuntimeExports.jsx("ul", { ref: listRef, className: "mds-select-options", children: filteredOptions.length === 0 ? (jsxRuntimeExports.jsx("li", { className: "mds-select-option mds-select-option--empty", children: "No options found" })) : (filteredOptions.map((option, index) => (jsxRuntimeExports.jsxs("li", { className: cn('mds-select-option', option.value === value && 'mds-select-option--selected', index === highlightedIndex && 'mds-select-option--highlighted', option.disabled && 'mds-select-option--disabled'), role: "option", "aria-selected": option.value === value, "aria-disabled": option.disabled, onClick: () => {
if (!option.disabled) {
handleSelect(option.value);
}
}, onMouseEnter: () => {
if (!option.disabled) {
setHighlightedIndex(index);
}
}, children: [jsxRuntimeExports.jsx("span", { className: "mds-select-option__label", children: option.label }), option.value === value && (jsxRuntimeExports.jsx("svg", { className: "mds-select-option__check", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z", clipRule: "evenodd" }) }))] }, option.value)))) })] }), document.body) : null;
return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [hiddenInput, jsxRuntimeExports.jsxs("button", { ref: combinedRef, type: "button", id: id, className: triggerClasses, onClick: () => isOpen ? closeDropdown() : openDropdown(), onKeyDown: handleKeyDown, disabled: disabled || loading, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-label": ariaLabel, "aria-invalid": error, children: [jsxRuntimeExports.jsx("span", { className: cn('mds-select-trigger__value', !selectedOption && 'mds-select-trigger__placeholder'), children: loading ? 'Loading...' : (selectedOption?.label || placeholder) }), jsxRuntimeExports.jsx("span", { className: "mds-select-trigger__icon", children: loading ? (jsxRuntimeExports.jsx("svg", { className: "mds-select-spinner", viewBox: "0 0 24 24", fill: "none", children: jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeDasharray: "32", strokeDashoffset: "32", children: jsxRuntimeExports.jsx("animate", { attributeName: "stroke-dashoffset", values: "32;0", dur: "1s", repeatCount: "indefinite" }) }) })) : (jsxRuntimeExports.jsx("svg", { className: "mds-select-chevron", viewBox: "0 0 20 20", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z", clipRule: "evenodd" }) })) })] }), dropdownContent] }));
});
Select.displayName = 'Select';
function ThemeToggle({ size = 'md', className = '', disabled = false, onChange, storageKey = 'theme' }) {
const [theme, setTheme] = require$$0.useState('dark');
const [mounted, setMounted] = require$$0.useState(false);
// Initialize theme on mount
require$$0.useEffect(() => {
try {
if (typeof window !== 'undefined') {
const savedTheme = localStorage.getItem(storageKey) || 'dark';
setTheme(savedTheme);
document.documentElement.setAttribute('data-theme', savedTheme);
setMounted(true);
}
}
catch (err) {
setTheme('dark');
setMounted(true);
}
}, [storageKey]);
// Listen to external theme changes
require$$0.useEffect(() => {
if (typeof window !== 'undefined') {
const updateTheme = () => {
const currentTheme = document.documentElement.getAttribute('data-theme') || 'dark';
setTheme(currentTheme);
};
const observer = new MutationObserver(() => {
updateTheme();
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
return () => observer.disconnect();
}
}, []);
const toggleTheme = () => {
if (disabled)
return;
try {
const newTheme = theme === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem(storageKey, newTheme);
setTheme(newTheme);
onChange?.(newTheme);
}
catch (err) {
console.warn('Failed to toggle theme:', err);
}
};
// Don't render until mounted (prevents hydration mismatch)
if (!mounted) {
return (jsxRuntimeExports.jsx("div", { className: `mds-theme-toggle mds-theme-toggle--${size} ${className}`, style: { opacity: 0.5 }, "aria-hidden": "true" }));
}
const isDark = theme === 'dark';
const buttonStyle = isDark
? {
backgroundColor: 'rgba(0, 0, 0, 0.35)',
border: '1px solid rgba(255, 255, 255, 0.18)',
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.3)'
}
: undefined;
const buttonClasses = [
'mds-theme-toggle',
`mds-theme-toggle--${size}`,
className
].filter(Boolean).join(' ');
return (jsxRuntimeExports.jsx("button", { onClick: toggleTheme, className: buttonClasses, disabled: disabled, type: "button", "aria-pressed": isDark, "aria-label": `Switch to ${isDark ? 'light' : 'dark'} mode`, title: `Switch to ${isDark ? 'light' : 'dark'} mode`, "data-theme": isDark ? 'dark' : 'light', style: buttonStyle }));
}
const TableHeader = ({ columns, gridTemplate, onSort, sortColumn, sortDirection, hasActions = false }) => {
return (jsxRuntimeExports.jsxs("div", { role: "rowgroup", style: {
display: 'grid',
gridTemplateColumns: gridTemplate,
gap: '16px',
padding: '16px 20px',
borderBottom: '1px solid rgba(var(--ink-rgb, 255, 255, 255), 0.08)',
backgroundColor: 'rgba(var(--ink-rgb, 255, 255, 255), 0.04)'
}, children: [columns.map((column) => (jsxRuntimeExports.jsxs("div", { role: "columnheader", style: {
color: 'var(--mds-color-text-primary, var(--text-primary))',
fontWeight: '500',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: column.align === 'center' ? 'center' :
column.align === 'right' ? 'flex-end' : 'flex-start',
cursor: column.sortable && onSort ? 'pointer' : 'default',
gap: '4px',
transition: 'color 0.2s ease'
}, onClick: () => column.sortable && onSort && onSort(column.key), onMouseEnter: (e) => {
if (column.sortable && onSort) {
e.currentTarget.style.color = 'var(--mds-color-text-primary, #F2F0EC)';
}
}, onMouseLeave: (e) => {
e.currentTarget.style.color = 'var(--mds-color-text-primary, var(--text-primary))';
}, children: [column.label, column.sortable && onSort && (jsxRuntimeExports.jsx("span", { style: {
fontSize: '12px',
opacity: sortColumn === column.key ? 1 : 0.5
}, children: sortColumn === column.key ?
(sortDirection === 'asc' ? '↑' : '↓') : '↕' }))] }, column.key))), hasActions && (jsxRuntimeExports.jsx("div", { role: "columnheader", style: {
color: 'var(--mds-color-text-primary, var(--text-primary))',
fontWeight: '500',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}, children: "Actions" }))] }));
};
const TableRow = ({ item, index, columns, actions = [], gridTemplate, isLast, variant, onActionClick }) => {
return (jsxRuntimeExports.jsxs("div", { role: "row", style: {
display: 'grid',
gridTemplateColumns: gridTemplate,
gap: '16px',
padding: variant === 'compact' ? '12px 20px' : '16px 20px',
borderBottom: isLast ? 'none' : '1px solid rgba(var(--ink-rgb, 255, 255, 255), 0.08)',
backgroundColor: 'transparent',
transition: 'background-color 0.2s ease'
}, onMouseEnter: (e) => {
e.currentTarget.style.backgroundColor = 'rgba(var(--ink-rgb, 255, 255, 255), 0.05)';
}, onMouseLeave: (e) => {
e.currentTarget.style.backgroundColor = 'transparent';
}, children: [columns.map((column) => (jsxRuntimeExports.jsx("div", { role: "cell", style: {
display: 'flex',
flexDirection: 'column',
gap: '4px',
justifyContent: 'center',
alignItems: column.align === 'center' ? 'center' :
column.align === 'right' ? 'flex-end' : 'flex-start'
}, children: column.render ?
column.render(item[column.key], item, index) :
jsxRuntimeExports.jsx("div", { style: {
color: 'var(--mds-color-text-primary, var(--text-primary))',
fontSize: '14px'
}, children: item[column.key] }) }, column.key))), actions.length > 0 && (jsxRuntimeExports.jsx("div", { role: "cell", style: {
display: 'flex',
gap: '8px',
justifyContent: 'center',
alignItems: 'center'
}, children: actions.map((action) => {
const isDisabled = action.disabled?.(item) || false;
const isLoading = action.loading?.(item) || false;
return (jsxRuntimeExports.jsxs("button", { onClick: () => !isDisabled && !isLoading && onActionClick(action, item), disabled: isDisabled || isLoading, style: {
background: 'none',
border: 'none',
borderRadius: '4px',
padding: '6px 8px',
color: action.variant === 'danger' ? 'var(--mds-color-error, #b42318)' : 'var(--mds-color-text-primary, var(--text-primary))',
fontSize: '12px',
cursor: isDisabled || isLoading ? 'not-allowed' : 'pointer',
display: 'flex',
alignItems: 'center',
gap: '4px',
transition: 'all 0.2s ease',
opacity: isDisabled || isLoading ? 0.5 : 1,
whiteSpace: 'nowrap'
}, onMouseEnter: (e) => {
if (!isDisabled && !isLoading) {
if (action.variant === 'danger') {
e.currentTarget.style.backgroundColor = 'rgba(180, 35, 24, 0.12)';
}
else {
e.currentTarget.style.backgroundColor = 'rgba(var(--ink-rgb, 255, 255, 255), 0.08)';
}
}
}, onMouseLeave: (e) => {
if (!isDisabled && !isLoading) {
e.currentTarget.style.backgroundColor = 'transparent';
}
}, children: [action.icon && action.icon, action.label] }, action.key));
}) }))] }));
};
const DataTable = ({ data, columns, loading = false, searchTerm = '', actions = [], variant = 'default', responsive = 'stack', onSort, emptyMessage = 'Nenhum item encontrado', loadingMessage = 'Carregando...', className, style, maxHeight = '600px', showSearchCount = true, rowHover = true, striped = false }) => {
const [sortColumn, setSortColumn] = require$$0.useState('');
const [sortDirection, setSortDirection] = require$$0.useState('asc');
// Filter data based on search term
const filteredData = require$$0.useMemo(() => {
if (!searchTerm)
return data;
return data.filter(item => {
return columns.some(column => {
const value = item[column.key];
if (value === null || value === undefined)
return false;
return String(value).toLowerCase().includes(searchTerm.toLowerCase());
});
});
}, [data, searchTerm, columns]);
// Generate grid template based on columns and actions
const gridTemplate = require$$0.useMemo(() => {
const columnWidths = columns.map(col => col.width || '1fr').join(' ');
const actionsWidth = actions.length > 0 ? ' 120px' : '';
return columnWidths + actionsWidth;
}, [columns, actions]);
// Handle sort
const handleSort = (columnKey) => {
let direction = 'asc';
if (sortColumn === columnKey && sortDirection === 'asc') {
direction = 'desc';
}
setSortColumn(columnKey);
setSortDirection(direction);
if (onSort) {
onSort(columnKey, direction);
}
};
// Handle action click
const handleActionClick = (action, item) => {
action.onClick(item);
};
// Loading state
if (loading) {
return (jsxRuntimeExports.jsx("div", { className: className, style: {
position: 'relative',
borderRadius: '2px',
backgroundColor: 'var(--mds-liquid-bg-card, #1C1C20)',
border: '1px solid var(--mds-liquid-border-subtle, transparent)',
boxShadow: 'var(--mds-liquid-shadow-raised, 0 4px 14px -8px rgba(0, 0, 0, 0.7))',
padding: '32px',
...style
}, children: jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '12px'
}, children: [jsxRuntimeExports.jsx("div", { style: {
width: '32px',
height: '32px',
border: '2px solid rgba(var(--ink-rgb, 255, 255, 255), 0.12)',
borderTop: '2px solid var(--mds-color-text-primary, #F2F0EC)',
borderRadius: '50%',
animation: 'spin 1s linear infinite'
} }), jsxRuntimeExports.jsx("span", { style: { color: 'var(--text-primary)' }, children: loadingMessage })] }) }));
}
// Empty state
if (filteredData.length === 0) {
return (jsxRuntimeExports.jsx("div", { className: className, style: {
position: 'relative',
borderRadius: '2px',
backgroundColor: 'var(--mds-liquid-bg-card, #1C1C20)',
border: '1px solid var(--mds-liquid-border-subtle, transparent)',
boxShadow: 'var(--mds-liquid-shadow-raised, 0 4px 14px -8px rgba(0, 0, 0, 0.7))',
padding: '40px',
textAlign: 'center',
...style
}, children: jsxRuntimeExports.jsx("div", { style: {
color: 'var(--text-secondary)',
fontSize: '16px'
}, children: emptyMessage }) }));
}
return (jsxRuntimeExports.jsxs("div", { className: className, style: {
position: 'relative',
borderRadius: '2px',
backgroundColor: 'var(--mds-liquid-bg-card, #1C1C20)',
border: '1px solid var(--mds-liquid-border-subtle, transparent)',
boxShadow: 'var(--mds-liquid-shadow-raised, 0 4px 14px -8px rgba(0, 0, 0, 0.7))',
overflow: 'hidden',
...style
}, role: "table", "aria-label": "Tabela de dados", children: [showSearchCount && searchTerm && (jsxRuntimeExports.jsxs("div", { style: {
padding: '16px',
color: 'var(--mds-color-text-secondary, var(--text-secondary))',
fontSize: '14px',
borderBottom: '1px solid rgba(var(--ink-rgb, 255, 255, 255), 0.08)'
}, children: ["Mostrando", ' ', jsxRuntimeExports.jsx("span", { style: { fontWeight: '500', color: 'var(--text-primary)' }, children: filteredData.length }), ' ', "de", ' ', jsxRuntimeExports.jsx("span", { style: { fontWeight: '500', color: 'var(--text-primary)' }, children: data.length }), ' ', "itens"] })), jsxRuntimeExports.jsx(TableHeader, { columns: columns, gridTemplate: gridTemplate, onSort: onSort ? handleSort : undefined, sortColumn: sortColumn, sortDirection: sortDirection, hasActions: actions.length > 0 }), jsxRuntimeExports.jsx("div", { role: "rowgroup", style: {
maxHeight,
overflowY: 'auto'
}, children: filteredData.map((item, index) => (jsxRuntimeExports.jsx(TableRow, { item: item, index: index, columns: columns, actions: actions, gridTemplate: gridTemplate, isLast: index === filteredData.length - 1, variant: variant, onActionClick: handleActionClick }, item.id || index))) })] }));
};
const CellRenderers = {
// ID cell - monospace background
id: (value) => (jsxRuntimeExports.jsx("div", { className: "data-table-code", style: {
color: 'var(--text-secondary)',
fontSize: '12px',
backgroundColor: 'rgba(255, 255, 255, 0.05)',
padding: '2px 8px',
borderRadius: '4px',
textAlign: 'center',
fontFamily: 'monospace'
}, children: value })),
// Name cell - primary text with background
name: (value) => (jsxRuntimeExports.jsx("div", { className: "data-table-code", style: {
color: 'var(--text-primary)',
fontSize: '14px',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
padding: '4px 12px',
borderRadius: '8px',
fontFamily: 'monospace'
}, children: value })),
// Parameter value - masked if secure
parameterValue: (value, item) => {
const displayValue = item.type === 'SecureString' ? '••••••••' :
(value?.length > 50 ? `${value.substring(0, 50)}...` : value);
return (jsxRuntimeExports.jsx("div", { className: "data-table-code", style: {
color: 'var(--text-primary)',
fontSize: '14px',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderRadius: '8px',
maxWidth: '300px',
wordBreak: 'break-all',
padding: '4px 12px',
fontFamily: 'monospace'
}, children: displayValue }));
},
// Badge renderer for types/statuses
badge: (value, variant = 'primary') => (jsxRuntimeExports.jsx("span", { className: `data-table-badge ${variant}`, children: value })),
// Parameter type badge
parameterType: (value) => {
const variant = value === 'SecureString' ? 'danger' :
value === 'StringList' ? 'success' : 'primary';
return CellRenderers.badge(value, variant);
},
// Environment badge
environment: (value, item) => {
const env = item.name?.includes('/prod/') ? 'PROD' : 'DEV';
const variant = env === 'PROD' ? 'info' : 'warning';
return CellRenderers.badge(env, variant);
},
// Action type badge for audit logs
actionType: (value) => {
const variant = value === 'CREATE' ? 'success' :
value === 'UPDATE' ? 'primary' : 'danger';
return CellRenderers.badge(value, variant);
},
// Date formatter
date: (value) => (jsxRuntimeExports.jsx("div", { style: {
color: 'var(--text-primary)',
fontSize: '14px'
}, children: value ? new Date(value).toLocaleString('pt-BR') : '-' })),
// Description/secondary text
description: (value) => (jsxRuntimeExports.jsx("div", { style: {
color: 'var(--text-secondary)',
fontSize: '12px',
maxWidth: '300px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}, children: value })),
// JSON preview for audit changes
jsonPreview: (value) => {
const jsonString = JSON.stringify(value);
const preview = jsonString.length > 30 ? jsonString.substring(0, 30) + '...' : jsonString;
return (jsxRuntimeExports.jsx("div", { style: {
color: 'var(--text-secondary)',
fontSize: '12px',
fontFamily: 'monospace',
maxWidth: '150px',
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}, children: preview }));
},
// Combined cell with main value and description
combined: (mainValue, description) => (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: [CellRenderers.name(mainValue), description && CellRenderers.description(description)] })),
// Combined with badges
combinedWithBadges: (mainValue, badges) => (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: [CellRenderers.name(mainValue), jsxRuntimeExports.jsx("div", { style: { display: 'flex', gap: '8px', flexWrap: 'wrap' }, children: badges.map((badge, index) => (jsxRuntimeExports.jsx("span", { className: `data-table-badge ${badge.variant || 'primary'}`, children: badge.value }, index))) })] }))
};
// Common action icons
const ActionIcons = {
edit: (jsxRuntimeExports.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { d: "M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z" }) })),
delete: (jsxRuntimeExports.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { d: "M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M7,6H17V19H7V6M9,8V17H11V8H9M13,8V17H15V8H13Z" }) })),
view: (jsxRuntimeExports.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", children: jsxRuntimeExports.jsx("path", { d: "M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" }) })),
loading: (jsxRuntimeExports.jsx("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "currentColor", className: "data-table-spinner", children: jsxRuntimeExports.jsx("path", { d: "M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" }) }))
};
// Configuração para ParametersTable (Settings)
const parametersTableConfig = {
columns: [
{
key: 'name',
label: 'Parâmetro',
width: '1fr',
render: (value, item) => CellRenderers.combinedWithBadges(value, [
{ value: item.type, variant: item.type === 'SecureString' ? 'danger' :
item.type === 'StringList' ? 'success' : 'primary' },
{ value: item.name.includes('/prod/') ? 'PROD' : 'DEV',
variant: item.name.includes('/prod/') ? 'info' : 'warning' }
])
},
{
key: 'value',
label: 'Valor',
width: '2fr',
render: (value, item) => (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: [CellRenderers.parameterValue(value, item), CellRenderers.description(item.description)] }))
}
],
actions: [
{
key: 'edit',
label: '✏️',
variant: 'secondary',
onClick: (item) => console.log('Edit', item),
disabled: (item) => false
},
{
key: 'delete',
label: '🗑️',
variant: 'danger',
onClick: (item) => console.log('Delete', item),
disabled: (item) => false,
loading: (item) => item.deleting || false
}
]
};
// Configuração para GeographyTable - Regions
const regionsTableConfig = {
columns: [
{
key: 'region_id',
label: 'ID',
width: '80px',
render: CellRenderers.id
},
{
key: 'name',
label: 'Nome',
width: '1fr',
render: CellRenderers.name
},
{
key: 'code',
label: 'Código',
width: '120px',
render: CellRenderers.name
},
{
key: 'created_at',
label: 'Criado em',
width: '140px',
render: CellRenderers.date
},
{
key: 'created_by',
label: 'Criado por',
width: '140px',
render: (value) => CellRenderers.name(value)
}
],
actions: [
{
key: 'edit',
label: '✏️',
variant: 'secondary',
onClick: (item) => console.log('Edit region', item)
},
{
key: 'delete',
label: '🗑️',
variant: 'danger',
onClick: (item) => console.log('Delete region', item),
loading: (item) => item.deleting || false
}
]
};
// Configuração para GeographyTable - Countries
const countriesTableConfig = {
columns: [
{
key: 'country_id',
label: 'ID',
width: '80px',
render: CellRenderers.id
},
{
key: 'name',
label: 'Nome',
width: '1fr',
render: CellRenderers.name
},
{
key: 'iso2',
label: 'Código',
width: '120px',
render: (value, item) => CellRenderers.combinedWithBadges(value, [
{ value: item.iso3, variant: 'primary' }
])
},
{
key: 'capital',
label: 'Capital',
width: '140px',
render: CellRenderers.name
},
{
key: 'region',
label: 'Região',
width: '120px',
render: (value) => CellRenderers.badge(value, 'info')
}
],
actions: [
{
key: 'edit',
label: '✏️',
variant: 'secondary',
onClick: (item) => console.log('Edit country', item)
},
{
key: 'delete',
label: '🗑️',
variant: 'danger',
onClick: (item) => console.log('Delete country', item)
}
]
};
// Configuração para Audit Log
const auditLogTableConfig = {
columns: [
{
key: 'timestamp',
label: 'Data/Hora',
width: '140px',
render: CellRenderers.date
},
{
key: 'user_email',
label: 'Usuário',
width: '180px',
render: CellRenderers.name
},
{
key: 'action',
label: 'Ação',
width: '100px',
render: CellRenderers.actionType
},
{
key: 'entity_type',
label: 'Tipo',
width: '120px',
render: (value) => CellRenderers.badge(value, 'primary')
},
{
key: 'entity_id',
label: 'ID',
width: '120px',
render: CellRenderers.description
},
{
key: 'changes',
label: 'Alterações',
width: '1fr',
render: (value, item) => (jsxRuntimeExports.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px' }, children: [CellRenderers.jsonPreview(value), jsxRuntimeExports.jsxs("button", { onClick: () => console.log('View details', item), style: {
background: 'none',
border: '1px solid var(--border-color)',
borderRadius: '4px',
padding: '4px 8px',
color: 'var(--text-primary)',
fontSize: '12px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '4px',
transition: 'all 0.2s ease',
whiteSpace: 'nowrap'
}, children: [ActionIcons.view, "Ver Detalhes"] })] }))
}
]
};
// Configuração para States
const statesTableConfig = {
columns: [
{
key: 'state_id',
label: 'ID',
width: '80px',
render: CellRenderers.id
},
{
key: 'name',
label: 'Nome',
width: '1fr',
render: CellRenderers.name
},
{
key: 'code',
label: 'Código',
width: '120px',
render: CellRenderers.name
},
{
key: 'country_name',
label: 'País',
width: '140px',
render: (value) => CellRenderers.badge(value, 'info')
}
],
actions: [
{
key: 'edit',
label: '✏️',
variant: 'secondary',
onClick: (item) => console.log('Edit state', item)
},
{
key: 'delete',
label: '🗑️',
variant: 'danger',
onClick: (item) => console.log('Delete state', item)
}
]
};
// Configuração para Cities
const citiesTableConfig = {
columns: [
{
key: 'city_id',
label: 'ID',
width: '80px',
render: CellRenderers.id
},
{
key: 'name',
label: 'Nome',
width: '1fr',
render: CellRenderers.name
},
{
key: 'population',
label: 'População',
width: '120px',
render: (value) => (jsxRuntimeExports.jsx("div", { style: { color: 'var(--text-primary)', fontSize: '14px' }, children: value ? value.toLocaleString('pt-BR') : '-' }))
},
{
key: 'state_name',
label: 'Estado',
width: '140px',
render: (value) => CellRenderers.badge(value, 'success')
},
{
key: 'country_name',
label: 'País',
width: '120px',
render: (value) => CellRenderers.badge(value, 'info')
}
],
actions: [
{
key: 'edit',
label: '✏️',
variant: 'secondary',
onClick: (item) => console.log('Edit city', item)
},
{
key: 'delete',
label: '🗑️',
variant: 'danger',
onClick: (item) => console.log('Delete city', item)
}
]
};
// Função helper para aplicar configuração baseada no tipo
const getTableConfig = (type) => {
switch (type) {
case 'parameters':
return parametersTableConfig;
case 'regions':
return regionsTableConfig;
case 'countries':
return countriesTableConfig;
case 'states':
return statesTableConfig;
case 'cities':
return citiesTableConfig;
case 'audit':
return auditLogTableConfig;
default:
return parametersTableConfig;
}
};
/**
* FormField - Normalized wrapper for form inputs
*
* Pattern extracted from AdminUserModal.tsx (backoffice/users)
* Provides consistent label, error, and helper text styling
*/
function FormField({ label, required = false, error, helper, disabled = false, children, className = '' }) {
return (jsxRuntimeExports.jsxs("div", { className: `mds-form-field ${className}`, style: { opacity: disabled ? 0.6 : 1 }, children: [jsxRuntimeExports.jsxs("label", { style: {
display: 'block',
marginBottom: '6px',
color: 'var(--mds-color-text-primary, var(--text-primary))',
fontSize: '0.9rem',
fontWeight: 500
}, children: [label, required && (jsxRuntimeExports.jsx("span", { style: { color: 'var(--mds-color-error, #ef4444)', marginLeft: '4px' }, children: "*" }))] }), children, error && (jsxRuntimeExports.jsx("span", { style: {
color: 'var(--mds-color-error, #ef4444)',
fontSize: '0.8rem',
marginTop: '4px',
display: 'block'
}, children: error })), helper && !error && (jsxRuntimeExports.jsx("span", { style: {
color: 'var(--mds-color-text-secondary, var(--text-secondary))',
fontSize: '0.75rem',
marginTop: '4px',
display: 'block'
}, children: helper }))] }));
}
const gapValues = {
sm: '12px',
md: '16px',
lg: '24px'
};
/**
* FormGrid - Normalized grid layout for form fields
*
* Pattern extracted from AdminUserModal.tsx (backoffice/users)
* Provides consistent 1 or 2 column layouts with proper gap
*/
function FormGrid({ columns = 1, gap = 'md', children, className = '' }) {
return (jsxRuntimeExports.jsx("div", { className: `mds-form-grid ${className}`, style: {
display: 'grid',
gridTemplateColumns: columns === 2 ? 'repeat(2, 1fr)' : '1fr',
gap: gapValues[gap]
}, children: children }));
}
/**
* FormSection - Normalized section wrapper for grouping form fields
*
* Pattern extracted from AdminUserModal.tsx (backoffice/users)
* Provides consistent section headers and spacing
*/
function FormSection({ title, description, children, className = '' }) {
return (jsxRuntimeExports.jsxs("div", { className: `mds-form-section ${className}`, style: {
marginBottom: '24px'
}, children: [title && (jsxRuntimeExports.jsx("h4", { style: {
margin: '0 0 8px 0',
color: 'var(--mds-color-text-primary, var(--text-primary))',
fontSize: '1rem',
fontWeight: 600,
letterSpacing: '-0.02em'
}, children: title })), description && (jsxRuntimeExports.jsx("p", { style: {
margin: '0 0 16px 0',
color: 'var(--mds-color-text-secondary, var(--text-secondary))',
fontSize: '0.85rem',
lineHeight: 1.5
}, children: description })), jsxRuntimeExports.jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '16px' }, children: children })] }));
}
const variantColors = {
default: {
bg: 'rgba(255, 255, 255, 0.03)',
border: 'rgba(255, 255, 255, 0.1)',
accent: 'var(--mds-color-text-secondary, #888)'
},
info: {
bg: 'rgba(59, 130, 246, 0.1)',
border: 'rgba(59, 130, 246, 0.2)',
accent: 'var(--mds-color-info, #3b82f6)'
},
success: {
bg: 'rgba(16, 185, 129, 0.1)',
border: 'rgba(16, 185, 129, 0.2)',
accent: 'var(--mds-color-success, #10b981)'
},
warning: {
bg: 'rgba(245, 158, 11, 0.1)',
border: 'rgba(245, 158, 11, 0.2)',
accent: 'var(--mds-color-warning, #f59e0b)'
},
danger: {
bg: 'rgba(239, 68, 68, 0.1)',
border: 'rgba(239, 68, 68, 0.2)',
accent: 'var(--mds-color-error, #ef4444)'
}
};
/**
* InfoBox - Normalized info/alert box for read-only information
*
* Pattern extracted from AdminUserModal.tsx (backoffice/users)
* Provides consistent info boxes for alerts, status displays, and read-only data
*
* @example
* // Alert style (with accent)
* <InfoBox variant="info" accent>
* An invitation email will be sent...
* </InfoBox>
*
* @example
* // Status display (default)
* <InfoBox title="User Information">
* <InfoRow label="Status" value="Active" />
* <InfoRow label="Created" value="Jan 1, 2024" />
* </InfoBox>
*/
function InfoBox({ title, variant = 'default', accent = false, copyable = false, children, className = '' }) {
const colors = variantColors[variant];
const handleCopy = () => {
if (!copyable)
return;
const text = typeof children === 'string' ? children : '';
if (text) {
navigator.clipboard.writeText(text);
}
};
return (jsxRuntimeExports.jsxs("div", { className: `mds-info-box ${className}`, style: {
padding: '12px 16px',
backgroundColor: colors.bg,
borderRadius: '8px',
border: `1px solid ${colors.border}`,
borderLeft: accent ? `4px solid ${colors.accent}` : `1px solid ${colors.border}`,
cursor: copyable ? 'pointer' : 'default'
}, onClick: copyable ? handleCopy : undefined, title: copyable ? 'Click to copy' : undefined, children: [title && (jsxRuntimeExports.jsx("div", { style: {
color: 'var(--mds-color-text-secondary, var(--text-secondary))',
fontSize: '0.85rem',
marginBottom: '8px',
fontWeight: 500
}, children: title })), jsxRuntimeExports.jsx("div", { style: {
color: 'var(--mds-color-text-primary, var(--text-primary))',
fontSize: '0.9rem',
lineHeight: 1.5
}, children: children })] }));
}
function InfoRow({ label, value, valueColor }) {
return (jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
justifyContent: 'space-between',
fontSize: '0.85rem',
padding: '2px 0'
}, children: [jsxRuntimeExports.jsxs("span", { style: { color: 'var(--mds-color-text-secondary, var(--text-secondary))' }, children: [label, ":"] }), jsxRuntimeExports.jsx("span", { style: { color: valueColor || 'var(--mds-color-text-primary, var(--text-primary))' }, children: value })] }));
}
/**
* Theme-aware styles generator — Meridian (flat, zero-border, opaque).
*
* Overlay: dark translucent scrim (no blur) in both themes.
* Card: opaque surface from Meridian tokens; flat shadow; no glass blur/specular.
* The `variant` ('glass' | 'solid') is preserved as a prop but both now resolve
* to the same flat/opaque Meridian surface (the 'glass' look is retired), and the
* theme is handled entirely by tokens — so this generator needs no arguments.
*/
function getStyles() {
return {
// Overlay behind the modal — dark translucent scrim, no blur (Meridian)
overlay: {
position: 'fixed',
inset: 0,
zIndex: 9999,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: 20,
// Dark scrim in both themes; token-driven with a safe fallback.
backgroundColor: 'var(--mds-liquid-bg-overlay, rgba(0, 0, 0, 0.6))',
},
// Modal card — flat, opaque Meridian surface
card: {
position: 'relative',
maxWidth: 640,
width: '100%',
borderRadius: 4,
overflow: 'hidden',
// Opaque surface (no transparency / no blur) from Meridian tokens.
background: 'var(--mds-liquid-bg-glass-thick, var(--mds-liquid-bg-card, #1C1C20))',
color: 'var(--mds-color-text-primary, inherit)',
// Zero-border: afford by shadow only.
border: '1px solid var(--mds-liquid-border-subtle, transparent)',
// Theme-aware Meridian elevation: shadow in light, subtle monochrome glow in dark.
boxShadow: 'var(--mds-modal-elevation, var(--mds-liquid-shadow-elevated, 0 16px 36px -18px rgba(0, 0, 0, 0.9)))',
transition: 'var(--mds-liquid-transition, all 0.2s cubic-bezier(0.25, 0.1, 0.25, 1))',
},
};
}
function Modal({ open, onClose, closeOnOverlay = true, children, className, style,
// `variant` ('glass' | 'solid') is retained in ModalProps for API stability,
// but Meridian renders both flat/opaque via tokens — it no longer affects style.
}) {
const [mounted, setMounted] = require$$0.useState(false);
const [visible, setVisible] = require$$0.useState(false);
const [renderPortal, setRenderPortal] = require$$0.useState(false);
const containerRef = require$$0.useRef(null);
require$$0.useEffect(() => setMounted(true), []);
require$$0.useEffect(() => {
if (!mounted)
return;
if (open) {
setVisible(true);
setRenderPortal(true);
}
else {
setVisible(false);
const t = setTimeout(() => setRenderPortal(false), 200);
return () => clearTimeout(t);
}
}, [open, mounted]);
require$$0.useEffect(() => {
if (!open)
return;
const onKeyDown = (e) => {
if (e.key === 'Escape')
onClose();
};
document.addEventListener('keydown', onKeyDown);
return () => document.removeEventListener('keydown', onKeyDown);
}, [open, onClose]);
if (!mounted || (!renderPortal && !open))
return null;
// Meridian styles are token-driven (theme + variant resolved by CSS vars).
const styles = getStyles();
const overlayStyle = {
...styles.overlay,
opacity: visible ? 1 : 0,
transition: 'opacity 200ms ease',
};
const cardStyle = {
...styles.card,
// Meridian: flat entrance — opacity fade only (no scale/translate).
opacity: visible ? 1 : 0,
transition: 'opacity 200ms ease',
...style,
};
const content = (jsxRuntimeExports.jsx("div", { className: "modal-overlay mds-modal-overlay", style: overlayStyle, onClick: closeOnOverlay ? onClose : undefined, "aria-modal": "true", role: "dialog", children: jsxRuntimeExports.jsx("div", { className: className, style: cardStyle, onClick: (e) => e.stopPropagation(), ref: (el) => (containerRef.current = el), children: children }) }));
return reactDom.createPortal(content, document.body);
}
const sizeMap$1 = {
sm: '400px',
md: '480px',
lg: '600px',
xl: '800px'
};
/**
* FormModal - Normalized template for CRUD modal forms
*
* Pattern extracted from AdminUserModal.tsx (backoffice/users)
* Provides consistent structure for create/edit entity modals
*
* @example
* <FormModal
* open={showModal}
* onClose={() => setShowModal(false)}
* onSubmit={handleSubmit}
* title="Edit User"
* info="Changes will be saved immediately."
* infoVariant="info"
* loading={isSaving}
* submitText="Save"
* >
* <FormField label="Name" required error={errors.name}>
* <input className="mds-input" value={name} onChange={...} />
* </FormField>
* </FormModal>
*/
function FormModal({ open, onClose, onSubmit, title, icon, subtitle, info, infoVariant = 'info', children, submitText = 'Save', cancelText = 'Cancel', loading = false, submitDisabled = false, size = 'md', className = '' }) {
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(e);
};
return (jsxRuntimeExports.jsxs(Modal, { open: open, onClose: onClose, closeOnOverlay: !loading, className: className, style: {
maxWidth: sizeMap$1[size],
maxHeight: '90vh',
overflowY: 'auto',
padding: '24px'
}, children: [jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: subtitle ? '8px' : '24px'
}, children: [jsxRuntimeExports.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '12px' }, children: [icon && (jsxRuntimeExports.jsx("div", { style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 32,
height: 32,
fontSize: '1.25rem',
lineHeight: 0,
opacity: 0.5
}, children: icon })), jsxRuntimeExports.jsx(Typography, { variant: "h3", color: "primary", style: { margin: 0 }, children: title })] }), jsxRuntimeExports.jsx("button", { type: "button", onClick: onClose, disabled: loading, style: {
background: 'none',
border: 'none',
padding: '8px',
cursor: loading ? 'not-allowed' : 'pointer',
color: 'var(--mds-color-text-secondary)',
opacity: loading ? 0.5 : 1,
borderRadius: '4px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'all 0.2s ease'
}, onMouseEnter: (e) => {
if (!loading)
e.currentTarget.style.backgroundColor = 'rgba(var(--ink-rgb, 255, 255, 255), 0.08)';
}, onMouseLeave: (e) => {
e.currentTarget.style.backgroundColor = 'transparent';
}, "aria-label": "Close modal", children: jsxRuntimeExports.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntimeExports.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] }), subtitle && (jsxRuntimeExports.jsx(Typography, { variant: "body", color: "secondary", style: { marginBottom: '24px', fontSize: '0.9rem' }, children: subtitle })), info && (jsxRuntimeExports.jsx("div", { style: { marginBottom: '20px' }, children: jsxRuntimeExports.jsx(InfoBox, { variant: infoVariant, accent: true, children: info }) })), jsxRuntimeExports.jsxs("form", { onSubmit: handleSubmit, style: { display: 'flex', flexDirection: 'column', gap: '16px' }, children: [children, jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
gap: '12px',
justifyContent: 'flex-end',
marginTop: '16px',
paddingTop: '16px',
borderTop: '1px solid rgba(var(--ink-rgb, 255, 255, 255), 0.08)'
}, children: [jsxRuntimeExports.jsx(Button, { variant: "secondary", size: "sm", type: "button", onClick: onClose, disabled: loading, style: { minWidth: '90px' }, children: cancelText }), jsxRuntimeExports.jsx(Button, { variant: "primary", size: "sm", type: "submit", loading: loading, disabled: loading || submitDisabled, style: { minWidth: '110px' }, children: submitText })] })] })] }));
}
const sizeMap = {
sm: '400px',
md: '480px',
lg: '600px',
xl: '800px'
};
/**
* DetailModal - Normalized template for read-only detail modals
*
* Pattern extracted from GeographyDetailsModal, AuditLogDetailModal (backoffice)
* Provides consistent structure for viewing entity details
*
* @example
* <DetailModal
* open={showDetails}
* onClose={() => setShowDetails(false)}
* title="User Details"
* subtitle="Created on Jan 1, 2024"
* action={{
* label: 'Edit',
* onClick: handleEdit,
* variant: 'primary'
* }}
* >
* <InfoBox title="Basic Information">
* <InfoRow label="Name" value={user.name} />
* <InfoRow label="Email" value={user.email} />
* </InfoBox>
* </DetailModal>
*/
function DetailModal({ open, onClose, title, icon, subtitle, children, closeText = 'Close', action, size = 'md', className = '' }) {
return (jsxRuntimeExports.jsxs(Modal, { open: open, onClose: onClose, closeOnOverlay: true, className: className, style: {
maxWidth: sizeMap[size],
maxHeight: '90vh',
overflowY: 'auto',
padding: '24px'
}, children: [jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: subtitle ? '8px' : '24px'
}, children: [jsxRuntimeExports.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '12px' }, children: [icon && (jsxRuntimeExports.jsx("div", { style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: 32,
height: 32,
fontSize: '1.25rem',
lineHeight: 0,
opacity: 0.5
}, children: icon })), jsxRuntimeExports.jsx(Typography, { variant: "h3", color: "primary", style: { margin: 0 }, children: title })] }), jsxRuntimeExports.jsx("button", { type: "button", onClick: onClose, style: {
background: 'none',
border: 'none',
padding: '8px',
cursor: 'pointer',
color: 'var(--mds-color-text-secondary)',
borderRadius: '4px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'all 0.2s ease'
}, onMouseEnter: (e) => {
e.currentTarget.style.backgroundColor = 'rgba(var(--ink-rgb, 255, 255, 255), 0.08)';
}, onMouseLeave: (e) => {
e.currentTarget.style.backgroundColor = 'transparent';
}, "aria-label": "Close modal", children: jsxRuntimeExports.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntimeExports.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntimeExports.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) })] }), subtitle && (jsxRuntimeExports.jsx(Typography, { variant: "body", color: "secondary", style: { marginBottom: '24px', fontSize: '0.9rem' }, children: subtitle })), jsxRuntimeExports.jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: '16px' }, children: children }), jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
gap: '12px',
justifyContent: 'flex-end',
marginTop: '24px',
paddingTop: '16px',
borderTop: '1px solid rgba(var(--ink-rgb, 255, 255, 255), 0.08)'
}, children: [jsxRuntimeExports.jsx(Button, { variant: "secondary", size: "sm", onClick: onClose, children: closeText }), action && (jsxRuntimeExports.jsx(Button, { variant: action.variant || 'primary', size: "sm", onClick: action.onClick, disabled: action.disabled, children: action.label }))] })] }));
}
// =============================================================================
// Icons
// =============================================================================
const Icons = {
shield: (jsxRuntimeExports.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: jsxRuntimeExports.jsx("path", { d: "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" }) })),
checkCircle: (jsxRuntimeExports.jsxs("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntimeExports.jsx("path", { d: "M22 11.08V12a10 10 0 1 1-5.93-9.14" }), jsxRuntimeExports.jsx("polyline", { points: "22 4 12 14.01 9 11.01" })] })),
circle: (jsxRuntimeExports.jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }) })),
};
function VerificationCheckItem({ check, action, variant }) {
if (variant === 'compact') {
// Compact version for popup - just icon + text
return (jsxRuntimeExports.jsxs("div", { className: "mds-verification-check-item mds-verification-check-item--compact", children: [jsxRuntimeExports.jsx("span", { className: `mds-verification-check-icon ${check.passed ? 'mds-verification-check-icon--passed' : ''}`, children: check.passed ? '\u2713' : '\u2717' }), jsxRuntimeExports.jsx("span", { className: "mds-verification-check-label", children: check.description })] }));
}
// Card version - with points and optional action
return (jsxRuntimeExports.jsxs("div", { className: `mds-verification-check-item ${check.passed ? 'mds-verification-check-item--passed' : ''}`, children: [jsxRuntimeExports.jsx("div", { className: "mds-verification-check-status", children: check.passed ? Icons.checkCircle : Icons.circle }), jsxRuntimeExports.jsxs("div", { className: "mds-verification-check-content", children: [jsxRuntimeExports.jsx("span", { className: "mds-verification-check-description", children: check.description }), jsxRuntimeExports.jsx("span", { className: "mds-verification-check-points", children: check.passed
? `${check.points ?? 1} point${(check.points ?? 1) !== 1 ? 's' : ''}`
: '0 points' })] }), action && (action.alwaysShow || !check.passed) && (action.renderCustom ? action.renderCustom : (jsxRuntimeExports.jsx(Button, { variant: check.passed ? 'ghost' : 'primary', size: "sm", onClick: action.onClick, disabled: action.disabled, children: action.label })))] }));
}
// =============================================================================
// Main Component
// =============================================================================
function VerificationScoreCard({ score, maxScore = 5, checks, loading = false, error = null, variant = 'card', getCheckAction, showInfoText = true, className = '', registrationSequence, memberSince, }) {
if (variant === 'compact') {
// Compact popup version (used in ProfileCard)
return (jsxRuntimeExports.jsxs("div", { className: `mds-verification-popup ${className}`, children: [jsxRuntimeExports.jsxs("div", { className: "mds-verification-popup__header", children: ["Verification Level ", loading ? '...' : score] }), jsxRuntimeExports.jsx("div", { className: "mds-verification-popup__checks", children: checks.map((check) => (jsxRuntimeExports.jsx(VerificationCheckItem, { check: check, variant: "compact" }, check.name))) }), (registrationSequence || memberSince) && (jsxRuntimeExports.jsxs("div", { className: "mds-verification-popup__footer", children: [registrationSequence && (jsxRuntimeExports.jsxs("span", { className: "mds-verification-popup__sequence", children: ["User #", registrationSequence.toLocaleString()] })), memberSince && (jsxRuntimeExports.jsxs("span", { className: "mds-verification-popup__date", children: ["since ", memberSince] }))] }))] }));
}
// Full card version (used in settings pages)
return (jsxRuntimeExports.jsxs("div", { className: `mds-verification-card ${className}`, children: [jsxRuntimeExports.jsxs("div", { className: "mds-verification-card__header", children: [jsxRuntimeExports.jsxs("div", { className: "mds-verification-card__badge", children: [jsxRuntimeExports.jsx("span", { className: "mds-verification-card__badge-label", children: "Verification Level" }), jsxRuntimeExports.jsx("span", { className: "mds-verification-card__badge-score", children: loading ? '...' : score })] }), jsxRuntimeExports.jsxs("div", { className: "mds-verification-card__title", children: [jsxRuntimeExports.jsx("h4", { className: "mds-verification-card__heading", children: "Verification Score" }), jsxRuntimeExports.jsx("p", { className: "mds-verification-card__subtitle", children: "Each completed criterion adds 1 point to your verification score." })] })] }), error && (jsxRuntimeExports.jsx("div", { className: "mds-verification-card__error", children: error })), jsxRuntimeExports.jsx("div", { className: "mds-verification-card__checks", children: checks.map((check) => (jsxRuntimeExports.jsx(VerificationCheckItem, { check: check, action: getCheckAction?.(check), variant: "card" }, check.name))) }), showInfoText && (jsxRuntimeExports.jsx("p", { className: "mds-verification-card__info", children: "The Verification Score indicates your account's trustworthiness. Users with higher scores have more credibility on the platform." }))] }));
}
// =============================================================================
// Constants
// =============================================================================
const EVENT_TYPE_LABELS = {
email_verified: 'Email Verified',
mfa_enabled: 'MFA Enabled',
mfa_disabled: 'MFA Disabled',
phone_verified: 'Phone Verified',
document_verified: 'Document Verified',
document_unverified: 'Document Unverified',
location_verified: 'Location Verified',
regular_activity: 'Regular Activity',
referral_verified: 'Referral Verified',
initial_score: 'Initial Score',
};
const EVENT_TYPE_COLORS = {
email_verified: '#22c55e', // green-500
mfa_enabled: '#3b82f6', // blue-500
mfa_disabled: '#ef4444', // red-500
phone_verified: '#a855f7', // purple-500
document_verified: '#14b8a6', // teal-500
document_unverified: '#f97316', // orange-500
location_verified: '#06b6d4', // cyan-500
regular_activity: '#8b5cf6', // violet-500
referral_verified: '#eab308', // yellow-500
initial_score: '#6b7280', // gray-500
};
// =============================================================================
// Helper Functions
// =============================================================================
function formatDate(isoString) {
const date = new Date(isoString);
return date.toLocaleDateString('pt-BR', {
day: '2-digit',
month: 'short',
year: 'numeric',
});
}
function formatDateTime(isoString) {
const date = new Date(isoString);
return date.toLocaleString('pt-BR', {
day: '2-digit',
month: 'short',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
}
function getEventLabel(eventType) {
return EVENT_TYPE_LABELS[eventType] || eventType.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
}
function getEventColor(eventType) {
return EVENT_TYPE_COLORS[eventType] || '#6b7280';
}
function isPositiveEvent(eventType) {
return !eventType.includes('disabled') && !eventType.includes('unverified');
}
function TimelineChart({ records, height }) {
const chartData = require$$0.useMemo(() => {
// Reverse to get chronological order (oldest first)
const chronological = [...records].reverse();
if (chronological.length === 0) {
return { points: [], lines: [], maxScore: 7 };
}
const maxScore = Math.max(...chronological.map((r) => r.max_score), 7);
const width = 100; // percentage
const padding = { top: 20, bottom: 30, left: 10, right: 10 };
const chartWidth = width - padding.left - padding.right;
const chartHeight = height - padding.top - padding.bottom;
// Calculate x and y positions
const points = chronological.map((record, index) => {
const x = padding.left + (index / Math.max(chronological.length - 1, 1)) * chartWidth;
const y = padding.top + ((maxScore - record.score) / maxScore) * chartHeight;
return {
x,
y,
record,
color: getEventColor(record.event_type),
};
});
// Create line path
const linePath = points.length > 1
? `M ${points.map((p) => `${p.x} ${p.y}`).join(' L ')}`
: '';
return { points, linePath, maxScore, padding, chartHeight, chartWidth };
}, [records, height]);
if (records.length === 0) {
return (jsxRuntimeExports.jsx("div", { className: "mds-timeline-chart mds-timeline-chart--empty", children: jsxRuntimeExports.jsx("p", { children: "No score history available yet." }) }));
}
const { points, linePath, maxScore, padding, chartHeight: ch } = chartData;
return (jsxRuntimeExports.jsxs("svg", { className: "mds-timeline-chart", viewBox: `0 0 100 ${height}`, preserveAspectRatio: "none", style: { width: '100%', height: `${height}px` }, children: [Array.from({ length: maxScore + 1 }).map((_, i) => {
const y = padding.top + (i / maxScore) * ch;
return (jsxRuntimeExports.jsxs("g", { children: [jsxRuntimeExports.jsx("line", { x1: padding.left, x2: 100 - padding.right, y1: y, y2: y, stroke: "currentColor", strokeOpacity: "0.1", strokeWidth: "0.3" }), jsxRuntimeExports.jsx("text", { x: padding.left - 2, y: y, fill: "currentColor", fillOpacity: "0.5", fontSize: "3", textAnchor: "end", dominantBaseline: "middle", children: maxScore - i })] }, i));
}), linePath && (jsxRuntimeExports.jsx("path", { d: linePath, fill: "none", stroke: "url(#scoreGradient)", strokeWidth: "0.8", strokeLinecap: "round", strokeLinejoin: "round" })), jsxRuntimeExports.jsx("defs", { children: jsxRuntimeExports.jsxs("linearGradient", { id: "scoreGradient", x1: "0%", y1: "0%", x2: "100%", y2: "0%", children: [jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "#3b82f6" }), jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "#22c55e" })] }) }), points.map((point, index) => (jsxRuntimeExports.jsx("g", { children: jsxRuntimeExports.jsx("circle", { cx: point.x, cy: point.y, r: "2", fill: point.color, stroke: "var(--mds-bg-primary, #fff)", strokeWidth: "0.5", children: jsxRuntimeExports.jsxs("title", { children: [getEventLabel(point.record.event_type), ": Score ", point.record.score, '\n', formatDateTime(point.record.timestamp)] }) }) }, index))), points.length > 0 && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("text", { x: padding.left, y: height - 5, fill: "currentColor", fillOpacity: "0.5", fontSize: "2.5", textAnchor: "start", children: formatDate(points[0].record.timestamp) }), points.length > 1 && (jsxRuntimeExports.jsx("text", { x: 100 - padding.right, y: height - 5, fill: "currentColor", fillOpacity: "0.5", fontSize: "2.5", textAnchor: "end", children: formatDate(points[points.length - 1].record.timestamp) }))] }))] }));
}
function EventList({ records, maxEvents }) {
const displayRecords = records.slice(0, maxEvents);
return (jsxRuntimeExports.jsx("div", { className: "mds-timeline-events", children: displayRecords.map((record, index) => (jsxRuntimeExports.jsxs("div", { className: "mds-timeline-event", children: [jsxRuntimeExports.jsx("div", { className: "mds-timeline-event__indicator", style: {
backgroundColor: getEventColor(record.event_type),
} }), jsxRuntimeExports.jsxs("div", { className: "mds-timeline-event__content", children: [jsxRuntimeExports.jsxs("div", { className: "mds-timeline-event__header", children: [jsxRuntimeExports.jsx("span", { className: "mds-timeline-event__type", children: getEventLabel(record.event_type) }), jsxRuntimeExports.jsx("span", { className: `mds-timeline-event__badge ${isPositiveEvent(record.event_type) ? 'mds-timeline-event__badge--positive' : 'mds-timeline-event__badge--negative'}`, children: isPositiveEvent(record.event_type) ? '+1' : '-1' })] }), jsxRuntimeExports.jsxs("div", { className: "mds-timeline-event__meta", children: [jsxRuntimeExports.jsx("span", { className: "mds-timeline-event__date", children: formatDateTime(record.timestamp) }), jsxRuntimeExports.jsxs("span", { className: "mds-timeline-event__score", children: ["Score: ", record.score] })] })] })] }, index))) }));
}
// =============================================================================
// Main Component
// =============================================================================
function VerificationTimeline({ records, loading = false, chartHeight = 120, showEventList = true, maxEventsInList = 10, className = '', }) {
if (loading) {
return (jsxRuntimeExports.jsx("div", { className: `mds-verification-timeline mds-verification-timeline--loading ${className}`, children: jsxRuntimeExports.jsxs("div", { className: "mds-timeline-skeleton", children: [jsxRuntimeExports.jsx("div", { className: "mds-timeline-skeleton__chart", style: { height: chartHeight } }), showEventList && (jsxRuntimeExports.jsx("div", { className: "mds-timeline-skeleton__events", children: [1, 2, 3].map((i) => (jsxRuntimeExports.jsx("div", { className: "mds-timeline-skeleton__event" }, i))) }))] }) }));
}
return (jsxRuntimeExports.jsxs("div", { className: `mds-verification-timeline ${className}`, children: [jsxRuntimeExports.jsx("div", { className: "mds-timeline-chart-container", children: jsxRuntimeExports.jsx(TimelineChart, { records: records, height: chartHeight }) }), showEventList && records.length > 0 && (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [jsxRuntimeExports.jsx("div", { className: "mds-timeline-divider" }), jsxRuntimeExports.jsx("h4", { className: "mds-timeline-events-title", children: "Recent Events" }), jsxRuntimeExports.jsx(EventList, { records: records, maxEvents: maxEventsInList })] })), records.length === 0 && (jsxRuntimeExports.jsxs("div", { className: "mds-timeline-empty", children: [jsxRuntimeExports.jsx("p", { children: "No verification events recorded yet." }), jsxRuntimeExports.jsx("p", { className: "mds-timeline-empty__hint", children: "Complete verification steps to build your score history." })] }))] }));
}
function ModalHeader({ title, icon, onClose, closeAriaLabel = 'Close', align = 'center' }) {
const CloseIcon = (jsxRuntimeExports.jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": true, children: jsxRuntimeExports.jsx("path", { d: "M18.3 5.71a1 1 0 00-1.41 0L12 10.59 7.11 5.7A1 1 0 105.7 7.11L10.59 12l-4.9 4.89a1 1 0 101.42 1.42L12 13.41l4.89 4.9a1 1 0 001.42-1.42L13.41 12l4.9-4.89a1 1 0 000-1.4z" }) }));
if (align === 'center') {
const iconBox = {
position: 'absolute',
left: 16,
top: '50%',
transform: 'translateY(-50%)',
width: 32,
height: 32,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '1.25rem',
lineHeight: 0,
opacity: 0.5
};
const closeBox = {
position: 'absolute',
right: 16,
top: '50%',
transform: 'translateY(-50%)',
width: 32,
height: 32,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
background: 'transparent',
border: 'none',
padding: 0,
cursor: 'pointer',
lineHeight: 0
};
return (jsxRuntimeExports.jsxs("div", { className: "mds-modal-header", style: {
position: 'relative',
padding: '16px 24px',
marginBottom: 16,
borderBottom: '1px solid rgba(var(--ink-rgb, 255, 255, 255), 0.08)'
}, children: [icon && (jsxRuntimeExports.jsx("div", { "aria-hidden": true, style: iconBox, children: icon })), jsxRuntimeExports.jsx("div", { style: { textAlign: 'center' }, children: typeof title === 'string' ? (jsxRuntimeExports.jsx("h3", { style: { margin: 0 }, children: title })) : (title) }), onClose && (jsxRuntimeExports.jsx("button", { type: "button", "aria-label": closeAriaLabel, onClick: onClose, style: closeBox, children: CloseIcon }))] }));
}
return (jsxRuntimeExports.jsxs("div", { className: "mds-modal-header", style: {
display: 'flex',
alignItems: 'center',
gap: 12,
padding: '16px 24px',
marginBottom: 16,
borderBottom: '1px solid var(--border-color)'
}, children: [icon && (jsxRuntimeExports.jsx("div", { "aria-hidden": true, style: { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 32, height: 32, fontSize: '1.25rem', lineHeight: 0, opacity: 0.5 }, children: icon })), jsxRuntimeExports.jsx("div", { style: { margin: 0, fontSize: '1.125rem', fontWeight: 600, flex: 1 }, children: typeof title === 'string' ? (jsxRuntimeExports.jsx("h3", { style: { margin: 0 }, children: title })) : (title) }), onClose && (jsxRuntimeExports.jsx("button", { type: "button", "aria-label": closeAriaLabel, onClick: onClose, style: { marginLeft: 'auto', background: 'transparent', border: 'none', width: 32, height: 32, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', lineHeight: 0 }, children: CloseIcon }))] }));
}
function ModalBody({ children, style, className }) {
return (jsxRuntimeExports.jsx("div", { className: className, style: { padding: '16px 24px', ...style }, children: children }));
}
function ModalFooter({ children, align = 'end' }) {
const justify = align === 'start' ? 'flex-start' : align === 'center' ? 'center' : 'flex-end';
return (jsxRuntimeExports.jsx("div", { style: { display: 'flex', gap: 12, justifyContent: justify, borderTop: '1px solid var(--border-color)', padding: '16px 24px', marginTop: 8 }, children: children }));
}
// Calendar icon for header (wireframe style)
const CalendarIcon = (jsxRuntimeExports.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": true, children: [jsxRuntimeExports.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2", ry: "2" }), jsxRuntimeExports.jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }), jsxRuntimeExports.jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }), jsxRuntimeExports.jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })] }));
// FEAT-0390: English is the default UI language (ui-language-english). Callers
// may pass `months` / `weekdays` props to localize.
const MONTHS_EN = [
'January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'
];
const WEEKDAYS_EN = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
function getDaysInMonth(year, month) {
return new Date(year, month + 1, 0).getDate();
}
function getFirstDayOfMonth(year, month) {
return new Date(year, month, 1).getDay();
}
function DatePickerModal({ open, onClose, onConfirm, value, title = 'Select date', confirmText = 'Confirm', cancelText = 'Cancel', minDate, maxDate, minAge, helperText, months = MONTHS_EN, weekdays = WEEKDAYS_EN, noSelectionError = 'Select a date', }) {
// Parse initial value or use today
const initialDate = require$$0.useMemo(() => {
if (value) {
const [y, m, d] = value.split('-').map(Number);
return { year: y, month: m - 1, day: d };
}
const today = new Date();
return { year: today.getFullYear() - 18, month: today.getMonth(), day: today.getDate() };
}, [value]);
const [viewYear, setViewYear] = require$$0.useState(initialDate.year);
const [viewMonth, setViewMonth] = require$$0.useState(initialDate.month);
const [selectedDate, setSelectedDate] = require$$0.useState(value ? initialDate : null);
const [error, setError] = require$$0.useState(null);
const today = new Date();
today.setHours(0, 0, 0, 0);
// Calculate max date based on minAge if provided
const effectiveMaxDate = require$$0.useMemo(() => {
if (minAge) {
const maxAllowed = new Date();
maxAllowed.setFullYear(maxAllowed.getFullYear() - minAge);
return maxAllowed.toISOString().split('T')[0];
}
return maxDate;
}, [minAge, maxDate]);
const isDateDisabled = (year, month, day) => {
const date = new Date(year, month, day);
date.setHours(0, 0, 0, 0);
if (minDate) {
const min = new Date(minDate);
min.setHours(0, 0, 0, 0);
if (date < min)
return true;
}
if (effectiveMaxDate) {
const max = new Date(effectiveMaxDate);
max.setHours(0, 0, 0, 0);
if (date > max)
return true;
}
return false;
};
const handlePrevMonth = () => {
if (viewMonth === 0) {
setViewMonth(11);
setViewYear(viewYear - 1);
}
else {
setViewMonth(viewMonth - 1);
}
};
const handleNextMonth = () => {
if (viewMonth === 11) {
setViewMonth(0);
setViewYear(viewYear + 1);
}
else {
setViewMonth(viewMonth + 1);
}
};
const handlePrevYear = () => setViewYear(viewYear - 1);
const handleNextYear = () => setViewYear(viewYear + 1);
const handleDayClick = (day) => {
if (isDateDisabled(viewYear, viewMonth, day))
return;
setSelectedDate({ year: viewYear, month: viewMonth, day });
setError(null);
};
const handleConfirm = () => {
if (!selectedDate) {
setError(noSelectionError);
return;
}
const dateStr = `${selectedDate.year}-${String(selectedDate.month + 1).padStart(2, '0')}-${String(selectedDate.day).padStart(2, '0')}`;
onConfirm(dateStr);
onClose();
};
const daysInMonth = getDaysInMonth(viewYear, viewMonth);
const firstDay = getFirstDayOfMonth(viewYear, viewMonth);
// Build calendar grid (always 42 cells = 6 weeks for consistent height)
const calendarDays = [];
for (let i = 0; i < firstDay; i++) {
calendarDays.push(null);
}
for (let d = 1; d <= daysInMonth; d++) {
calendarDays.push(d);
}
// Fill remaining cells to always have 42 (6 rows x 7 cols)
while (calendarDays.length < 42) {
calendarDays.push(null);
}
// Styles
const calendarStyles = {
container: {
display: 'flex',
flexDirection: 'column',
gap: 16,
},
navigation: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 8,
},
navButton: {
background: 'transparent',
border: '1px solid rgba(var(--ink-rgb, 128, 128, 128), 0.18)',
borderRadius: 4,
width: 36,
height: 36,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
color: 'var(--mds-color-text-primary, inherit)',
transition: 'background 0.15s ease',
},
monthYear: {
display: 'flex',
alignItems: 'center',
gap: 8,
fontSize: '1rem',
fontWeight: 600,
},
weekdays: {
display: 'grid',
gridTemplateColumns: 'repeat(7, 1fr)',
gap: 4,
textAlign: 'center',
fontSize: '0.75rem',
fontWeight: 500,
opacity: 0.6,
padding: '8px 0',
borderBottom: '1px solid rgba(var(--ink-rgb, 128, 128, 128), 0.18)',
},
daysGrid: {
display: 'grid',
gridTemplateColumns: 'repeat(7, 1fr)',
gridTemplateRows: 'repeat(6, 1fr)',
gap: 4,
minHeight: 240,
},
dayCell: {
aspectRatio: '1',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 4,
fontSize: '0.875rem',
cursor: 'pointer',
transition: 'all 0.15s ease',
border: '1px solid transparent',
},
helper: {
fontSize: '0.75rem',
color: 'var(--mds-color-text-secondary, var(--mds-color-text-muted, rgba(128,128,128,0.7)))',
textAlign: 'center',
padding: '8px 0 0',
},
error: {
fontSize: '0.75rem',
color: 'var(--mds-color-danger, #ef4444)',
textAlign: 'center',
padding: '8px 0 0',
},
};
return (jsxRuntimeExports.jsxs(Modal, { open: open, onClose: onClose, style: { maxWidth: 380, width: '100%' }, children: [jsxRuntimeExports.jsx(ModalHeader, { title: title, icon: CalendarIcon, onClose: onClose, align: "center" }), jsxRuntimeExports.jsx(ModalBody, { children: jsxRuntimeExports.jsxs("div", { style: calendarStyles.container, children: [jsxRuntimeExports.jsxs("div", { style: calendarStyles.navigation, children: [jsxRuntimeExports.jsx("button", { type: "button", style: calendarStyles.navButton, onClick: handlePrevYear, "aria-label": "Previous year", children: jsxRuntimeExports.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntimeExports.jsx("polyline", { points: "11 17 6 12 11 7" }), jsxRuntimeExports.jsx("polyline", { points: "18 17 13 12 18 7" })] }) }), jsxRuntimeExports.jsx("div", { style: calendarStyles.monthYear, children: jsxRuntimeExports.jsx("span", { children: viewYear }) }), jsxRuntimeExports.jsx("button", { type: "button", style: calendarStyles.navButton, onClick: handleNextYear, "aria-label": "Next year", children: jsxRuntimeExports.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [jsxRuntimeExports.jsx("polyline", { points: "13 17 18 12 13 7" }), jsxRuntimeExports.jsx("polyline", { points: "6 17 11 12 6 7" })] }) })] }), jsxRuntimeExports.jsxs("div", { style: calendarStyles.navigation, children: [jsxRuntimeExports.jsx("button", { type: "button", style: calendarStyles.navButton, onClick: handlePrevMonth, "aria-label": "Previous month", children: jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntimeExports.jsx("polyline", { points: "15 18 9 12 15 6" }) }) }), jsxRuntimeExports.jsx("div", { style: calendarStyles.monthYear, children: jsxRuntimeExports.jsx("span", { children: months[viewMonth] }) }), jsxRuntimeExports.jsx("button", { type: "button", style: calendarStyles.navButton, onClick: handleNextMonth, "aria-label": "Next month", children: jsxRuntimeExports.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntimeExports.jsx("polyline", { points: "9 18 15 12 9 6" }) }) })] }), jsxRuntimeExports.jsx("div", { style: calendarStyles.weekdays, children: weekdays.map((day) => (jsxRuntimeExports.jsx("div", { children: day }, day))) }), jsxRuntimeExports.jsx("div", { style: calendarStyles.daysGrid, children: calendarDays.map((day, idx) => {
if (day === null) {
return jsxRuntimeExports.jsx("div", {}, `empty-${idx}`);
}
const isSelected = selectedDate &&
selectedDate.year === viewYear &&
selectedDate.month === viewMonth &&
selectedDate.day === day;
const isDisabled = isDateDisabled(viewYear, viewMonth, day);
const isToday = viewYear === today.getFullYear() &&
viewMonth === today.getMonth() &&
day === today.getDate();
const cellStyle = {
...calendarStyles.dayCell,
background: isSelected
? 'var(--mds-color-accent, var(--mds-color-brand-primary, #2563eb))'
: 'transparent',
color: isSelected
? 'var(--on-accent, #fff)'
: isDisabled
? 'var(--mds-color-text-tertiary, var(--mds-color-text-muted, rgba(128,128,128,0.5)))'
: 'var(--mds-color-text-primary, inherit)',
cursor: isDisabled ? 'not-allowed' : 'pointer',
opacity: isDisabled ? 0.4 : 1,
border: isToday && !isSelected
? '1px solid var(--mds-color-accent, var(--mds-color-brand-primary, #2563eb))'
: '1px solid transparent',
fontWeight: isToday ? 600 : 400,
};
return (jsxRuntimeExports.jsx("button", { type: "button", style: cellStyle, onClick: () => handleDayClick(day), disabled: isDisabled, "aria-label": `${months[viewMonth]} ${day}, ${viewYear}`, "aria-pressed": isSelected || undefined, children: day }, day));
}) }), error && jsxRuntimeExports.jsx("div", { style: calendarStyles.error, children: error }), !error && helperText && jsxRuntimeExports.jsx("div", { style: calendarStyles.helper, children: helperText })] }) }), jsxRuntimeExports.jsxs(ModalFooter, { children: [jsxRuntimeExports.jsx(Button, { variant: "secondary", onClick: onClose, children: cancelText }), jsxRuntimeExports.jsx(Button, { variant: "primary", onClick: handleConfirm, disabled: !selectedDate, children: confirmText })] })] }));
}
// =============================================================================
// Component
// =============================================================================
function JourneyProgress({ tracks, activeTrackId, caption, captionAside, className, }) {
return (jsxRuntimeExports.jsxs("div", { className: ['mds-journey-progress', className].filter(Boolean).join(' '), "aria-label": "Journey progress", children: [(caption || captionAside) && (jsxRuntimeExports.jsxs("div", { className: "mds-journey-progress__head", children: [caption && jsxRuntimeExports.jsx("span", { className: "mds-journey-progress__caption", children: caption }), captionAside] })), jsxRuntimeExports.jsx("div", { className: "mds-journey-progress__tracks", children: tracks.map((track) => {
const total = Math.max(1, track.total);
const completed = Math.min(Math.max(0, track.completed), total);
const active = track.id === activeTrackId;
return (jsxRuntimeExports.jsxs("div", { className: `mds-journey-track${active ? ' mds-journey-track--active' : ''}`, "data-track": track.id, ...(active ? { 'aria-current': 'step' } : {}), children: [jsxRuntimeExports.jsxs("div", { className: "mds-journey-track__label", children: [jsxRuntimeExports.jsx("span", { children: track.label }), jsxRuntimeExports.jsxs("span", { children: [completed, "/", total] })] }), jsxRuntimeExports.jsx("div", { className: "mds-journey-track__rail", role: "progressbar", "aria-label": `${track.label} progress`, "aria-valuemin": 0, "aria-valuemax": total, "aria-valuenow": completed, children: jsxRuntimeExports.jsx("div", { className: "mds-journey-track__bar", style: { width: `${(completed / total) * 100}%` } }) })] }, track.id));
}) })] }));
}
// =============================================================================
// Component
// =============================================================================
function JourneyCard({ kicker, title, lede, metric, metricLabel, tone = 'neutral', children, actions, hideTitle = false, titleRef, titleId, className, }) {
return (jsxRuntimeExports.jsxs("div", { className: ['mds-journey-card', `mds-journey-card--${tone}`, className]
.filter(Boolean).join(' '), "data-tone": tone, children: [jsxRuntimeExports.jsx("div", { className: "mds-journey-card__facets", "aria-hidden": "true" }), jsxRuntimeExports.jsxs("div", { className: "mds-journey-card__inner", children: [jsxRuntimeExports.jsxs("header", { className: "mds-journey-card__header", children: [jsxRuntimeExports.jsxs("div", { className: "mds-journey-card__heading", children: [kicker && jsxRuntimeExports.jsx("p", { className: "mds-journey-card__kicker", children: kicker }), jsxRuntimeExports.jsx("h2", { id: titleId, ref: titleRef, tabIndex: -1, className: `mds-journey-card__title${hideTitle ? ' mds-visually-hidden' : ''}`, children: title }), lede && !hideTitle && jsxRuntimeExports.jsx("p", { className: "mds-journey-card__lede", children: lede })] }), metric !== undefined && metric !== null && (jsxRuntimeExports.jsxs("div", { className: "mds-journey-card__metric", children: [jsxRuntimeExports.jsx("strong", { children: metric }), metricLabel && jsxRuntimeExports.jsx("span", { children: metricLabel })] }))] }), children && jsxRuntimeExports.jsx("div", { className: "mds-journey-card__body", children: children })] }), actions && jsxRuntimeExports.jsx("div", { className: "mds-journey-card__actions", children: actions })] }));
}
// =============================================================================
// Component
// =============================================================================
function CodeInput({ value, onChange, length = 6, label = 'Code', onComplete, error, disabled = false, autoFocus = false, alphanumeric = false, id, className, }) {
const generatedId = require$$0.useId();
const inputId = id || `mds-code-${generatedId}`;
const errorId = `${inputId}-error`;
const inputRef = require$$0.useRef(null);
const sanitize = require$$0.useCallback((raw) => {
const pattern = alphanumeric ? /[^a-zA-Z0-9]/g : /[^0-9]/g;
const cleaned = raw.replace(pattern, '');
return (alphanumeric ? cleaned.toUpperCase() : cleaned).slice(0, length);
}, [alphanumeric, length]);
const handleChange = (event) => {
const next = sanitize(event.target.value);
onChange(next);
if (next.length === length)
onComplete?.(next);
};
const characters = Array.from({ length }, (_, index) => value[index] || '');
return (jsxRuntimeExports.jsxs("div", { className: ['mds-code-input', error ? 'mds-code-input--error' : '', className]
.filter(Boolean).join(' '), children: [jsxRuntimeExports.jsx("label", { className: "mds-code-input__label", htmlFor: inputId, children: label }), jsxRuntimeExports.jsxs("div", { className: "mds-code-input__field", onClick: () => inputRef.current?.focus(), children: [jsxRuntimeExports.jsx("input", { ref: inputRef, id: inputId, className: "mds-code-input__control", type: "text", inputMode: alphanumeric ? 'text' : 'numeric', autoComplete: "one-time-code", spellCheck: false, maxLength: length, value: value, onChange: handleChange, disabled: disabled, autoFocus: autoFocus, "aria-invalid": error ? true : undefined, "aria-describedby": error ? errorId : undefined }), jsxRuntimeExports.jsx("div", { className: "mds-code-input__slots", "aria-hidden": "true", children: characters.map((character, index) => (jsxRuntimeExports.jsx("span", { className: `mds-code-input__slot${index === value.length && !disabled ? ' mds-code-input__slot--next' : ''}`, children: character }, index))) })] }), jsxRuntimeExports.jsx("span", { className: "mds-code-input__error", id: errorId, "aria-live": "polite", children: error || '' })] }));
}
// =============================================================================
// Component
// =============================================================================
function ComboField({ label, value, onChange, options, placeholder, helper, error, disabled = false, loading = false, required = false, autoComplete = 'off', maxLength, id, className, adornment, }) {
const generatedId = require$$0.useId();
const fieldId = id || `mds-combo-${generatedId}`;
const listId = `${fieldId}-options`;
const errorId = `${fieldId}-error`;
const helperId = `${fieldId}-helper`;
const describedBy = [error ? errorId : null, helper ? helperId : null]
.filter(Boolean).join(' ') || undefined;
return (jsxRuntimeExports.jsxs("div", { className: ['mds-combo-field', error ? 'mds-combo-field--error' : '', className]
.filter(Boolean).join(' '), children: [jsxRuntimeExports.jsxs("label", { className: "mds-combo-field__label", htmlFor: fieldId, children: [label, required && jsxRuntimeExports.jsx("span", { className: "mds-combo-field__required", "aria-hidden": "true", children: " *" })] }), jsxRuntimeExports.jsxs("div", { className: "mds-combo-field__control", children: [jsxRuntimeExports.jsx("input", { id: fieldId, className: "mds-combo-field__input", type: "text", list: listId, value: value, onChange: (event) => onChange(event.target.value), placeholder: loading ? 'Loading…' : placeholder, disabled: disabled || loading, required: required, autoComplete: autoComplete, maxLength: maxLength, spellCheck: false, "aria-invalid": error ? true : undefined, "aria-describedby": describedBy }), adornment && jsxRuntimeExports.jsx("span", { className: "mds-combo-field__adornment", children: adornment }), jsxRuntimeExports.jsx("datalist", { id: listId, children: options.map((option) => (jsxRuntimeExports.jsx("option", { value: option.label ?? option.value, children: option.hint }, option.value))) })] }), helper && !error && (jsxRuntimeExports.jsx("span", { className: "mds-combo-field__helper", id: helperId, children: helper })), error && (jsxRuntimeExports.jsx("span", { className: "mds-combo-field__error", id: errorId, "aria-live": "polite", children: error }))] }));
}
// =============================================================================
// Component
// =============================================================================
function InvitationChip({ code, benefit, label = 'Invitation', onRemove, removeLabel = 'Remove invitation code', tone = 'premium', className, }) {
return (jsxRuntimeExports.jsxs("div", { className: ['mds-invitation-chip', `mds-invitation-chip--${tone}`, className]
.filter(Boolean).join(' '), children: [jsxRuntimeExports.jsx("span", { className: "mds-invitation-chip__mark", "aria-hidden": "true", children: jsxRuntimeExports.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", children: jsxRuntimeExports.jsx("path", { d: "M4 9h16v10H4zM3 9l3-5h12l3 5M12 4v15M4 13h16" }) }) }), jsxRuntimeExports.jsxs("span", { className: "mds-invitation-chip__copy", children: [jsxRuntimeExports.jsx("span", { children: label }), jsxRuntimeExports.jsx("strong", { children: code }), benefit && jsxRuntimeExports.jsx("small", { children: benefit })] }), onRemove && (jsxRuntimeExports.jsx("button", { type: "button", className: "mds-invitation-chip__remove", onClick: onRemove, "aria-label": removeLabel, title: removeLabel, children: jsxRuntimeExports.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", "aria-hidden": "true", children: jsxRuntimeExports.jsx("path", { d: "m7 7 10 10M17 7 7 17" }) }) }))] }));
}
// =============================================================================
// Component
// =============================================================================
function ChoiceTile({ label, meta, icon, selected = false, showMark = true, onClick, disabled = false, variant = 'outline', block = false, className, type = 'button', }) {
return (jsxRuntimeExports.jsxs("button", { type: type, className: [
'mds-choice-tile',
`mds-choice-tile--${variant}`,
selected ? 'mds-choice-tile--selected' : '',
block ? 'mds-choice-tile--block' : '',
className,
].filter(Boolean).join(' '), onClick: onClick, disabled: disabled, "aria-pressed": selected, children: [icon && jsxRuntimeExports.jsx("span", { className: "mds-choice-tile__icon", "aria-hidden": "true", children: icon }), jsxRuntimeExports.jsxs("span", { className: "mds-choice-tile__copy", children: [jsxRuntimeExports.jsx("span", { className: "mds-choice-tile__label", children: label }), meta && jsxRuntimeExports.jsx("span", { className: "mds-choice-tile__meta", children: meta })] }), showMark && (jsxRuntimeExports.jsx("span", { className: "mds-choice-tile__mark", "aria-hidden": "true", children: jsxRuntimeExports.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: jsxRuntimeExports.jsx("path", { d: "m5 12 4 4L19 6" }) }) }))] }));
}
const VERTICES = [
[1, 0, 0], [-1, 0, 0],
[0, 1, 0], [0, -1, 0],
[0, 0, 1], [0, 0, -1],
];
const subtract = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
const cross = (a, b) => [
a[1] * b[2] - a[2] * b[1],
a[2] * b[0] - a[0] * b[2],
a[0] * b[1] - a[1] * b[0],
];
const dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
const normalize = (v) => {
const length = Math.hypot(v[0], v[1], v[2]) || 1;
return [v[0] / length, v[1] / length, v[2] / length];
};
/** The 8 triangles, each wound so its normal points outward. */
const FACES = (() => {
const index = {
x: { '1': 0, '-1': 1 },
y: { '1': 2, '-1': 3 },
z: { '1': 4, '-1': 5 },
};
const faces = [];
[1, -1].forEach((sx) => {
[1, -1].forEach((sy) => {
[1, -1].forEach((sz) => {
let face = [index.x[String(sx)], index.y[String(sy)], index.z[String(sz)]];
const [a, b, c] = face.map((i) => VERTICES[i]);
const normal = cross(subtract(b, a), subtract(c, a));
const centroid = [
(a[0] + b[0] + c[0]) / 3,
(a[1] + b[1] + c[1]) / 3,
(a[2] + b[2] + c[2]) / 3,
];
if (dot(normal, centroid) < 0)
face = [face[0], face[2], face[1]];
faces.push(face);
});
});
});
return faces;
})();
const LIGHT = normalize([-0.55, 0.6, 0.58]);
const SOLIDS = [
{
scale: [1, 1, 1],
front: (light) => 0.10 + 0.90 * Math.pow(light, 1.3),
back: (light) => 0.05 + 0.16 * Math.pow(light, 1.3),
},
{
scale: [0.49, 0.97, 0.49],
front: (light) => 0.05 + 0.30 * Math.pow(light, 1.2),
back: (light) => 0.02 + 0.08 * light,
},
];
/** Project one frame of the solid at `angleY` radians. */
function octahedronFrame(angleY) {
const cosine = Math.cos(angleY);
const sine = Math.sin(angleY);
const rotateY = ([x, y, z]) => [x * cosine + z * sine, y, -x * sine + z * cosine];
const outerBack = [];
const inner = [];
const outerFront = [];
SOLIDS.forEach((solid, solidIndex) => {
const vertices = VERTICES.map(([x, y, z]) => rotateY([x * solid.scale[0], y * solid.scale[1], z * solid.scale[2]]));
FACES.forEach((face) => {
const points3d = face.map((i) => vertices[i]);
const normal = normalize(cross(subtract(points3d[1], points3d[0]), subtract(points3d[2], points3d[0])));
const depth = (points3d[0][2] + points3d[1][2] + points3d[2][2]) / 3;
const front = normal[2] > 0;
const litNormal = front ? normal : [-normal[0], -normal[1], -normal[2]];
const light = Math.max(0, dot(litNormal, LIGHT));
const points = points3d
.map(([x, y]) => `${(256 + 200 * x).toFixed(1)},${(256 - 200 * y).toFixed(1)}`)
.join(' ');
const polygon = {
points,
opacity: front ? solid.front(light) : solid.back(light),
depth,
};
if (solidIndex === 0)
(front ? outerFront : outerBack).push(polygon);
else
inner.push(polygon);
});
});
inner.sort((a, b) => a.depth - b.depth);
return [...outerBack, ...inner, ...outerFront];
}
// =============================================================================
// Component
// =============================================================================
function OctahedronMark({ step = 0, size = 220, period = 150, degreesPerStep = 0.7, scalePerStep = 0.006, className, }) {
const [angle, setAngle] = require$$0.useState(0);
const [reducedMotion, setReducedMotion] = require$$0.useState(true);
const frameRef = require$$0.useRef(null);
require$$0.useEffect(() => {
if (typeof window === 'undefined' || typeof window.matchMedia !== 'function')
return;
const query = window.matchMedia('(prefers-reduced-motion: reduce)');
const apply = () => setReducedMotion(query.matches);
apply();
query.addEventListener?.('change', apply);
return () => query.removeEventListener?.('change', apply);
}, []);
require$$0.useEffect(() => {
if (reducedMotion)
return undefined;
const velocity = (2 * Math.PI) / period;
const startedAt = performance.now();
let previous = 0;
const tick = (now) => {
frameRef.current = requestAnimationFrame(tick);
// ~30fps is plenty for a watermark and keeps the main thread free.
if (now - previous < 33)
return;
previous = now;
setAngle(velocity * ((now - startedAt) / 1000));
};
frameRef.current = requestAnimationFrame(tick);
return () => {
if (frameRef.current !== null)
cancelAnimationFrame(frameRef.current);
};
}, [reducedMotion, period]);
const polygons = require$$0.useMemo(() => octahedronFrame(angle), [angle]);
const twist = step * degreesPerStep;
const scale = 1 + step * scalePerStep;
return (jsxRuntimeExports.jsx("div", { className: ['mds-octahedron', className].filter(Boolean).join(' '), style: {
// Size travels as a custom property, not as an inline width/height, so
// a narrow host can cap it with plain CSS instead of fighting inline
// styles — an inline 230px mark overflowed the viewport at 360px.
['--mds-octahedron-size']: `${size}px`,
transform: `rotate(${twist}deg) scale(${scale})`,
}, "aria-hidden": "true", children: jsxRuntimeExports.jsx("svg", { viewBox: "0 0 512 512", focusable: "false", "aria-hidden": "true", children: polygons.map((polygon, index) => (jsxRuntimeExports.jsx("polygon", { fill: "currentColor", points: polygon.points, opacity: polygon.opacity.toFixed(3) }, index))) }) }));
}
// Wireframe header icon (20x20, question mark for all confirmations)
const HeaderIcon = (jsxRuntimeExports.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": true, children: [jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }), jsxRuntimeExports.jsx("path", { d: "M9 9c0-1.7 1.3-3 3-3s3 1.3 3 3c0 1-.5 1.8-1.2 2.3-.5.3-1 .7-1 1.2v.5", strokeLinecap: "round", strokeLinejoin: "round" }), jsxRuntimeExports.jsx("circle", { cx: "12", cy: "17", r: "0.5", fill: "currentColor" })] }));
/**
* Body icon component (56x56, wireframe Liquid Glass style)
* Uses neutral color matching text for theme compatibility
*/
function BodyIcon() {
return (jsxRuntimeExports.jsxs("svg", { width: "56", height: "56", viewBox: "0 0 48 48", fill: "none", "aria-hidden": true, style: {
flexShrink: 0,
color: 'var(--mds-color-text-secondary, rgba(0, 0, 0, 0.6))',
}, children: [jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: "22", stroke: "currentColor", strokeWidth: "1.5", opacity: "0.4" }), jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: "18", stroke: "currentColor", strokeWidth: "1", strokeDasharray: "4 2", opacity: "0.25" }), jsxRuntimeExports.jsx("path", { d: "M20 18c0-2.2 1.8-4 4-4s4 1.8 4 4c0 1.5-.8 2.5-2 3.2-.8.5-1.5 1-1.5 1.8v1", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", fill: "none", opacity: "0.7" }), jsxRuntimeExports.jsx("circle", { cx: "24.5", cy: "31", r: "1.5", fill: "currentColor", opacity: "0.7" })] }));
}
function ConfirmDialog({ open, onClose, title, description, confirmText = 'Confirm', cancelText = 'Cancel', onConfirm, loading = false, tone = 'warning', icon, disableOverlayClose, }) {
return (jsxRuntimeExports.jsxs(Modal, { open: open, onClose: onClose, closeOnOverlay: !disableOverlayClose, style: { maxWidth: 480, width: '100%' }, children: [jsxRuntimeExports.jsx(ModalHeader, { title: title, align: "center", onClose: onClose, icon: icon ?? HeaderIcon }), description && (jsxRuntimeExports.jsx(ModalBody, { children: jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
gap: 20,
padding: '12px 0'
}, children: [jsxRuntimeExports.jsx(BodyIcon, {}), jsxRuntimeExports.jsx(Typography, { variant: "body", color: "secondary", as: "span", children: description })] }) })), jsxRuntimeExports.jsxs(ModalFooter, { children: [jsxRuntimeExports.jsx(Button, { variant: "secondary", onClick: onClose, children: cancelText }), jsxRuntimeExports.jsx(Button, { variant: "primary", onClick: onConfirm, loading: loading, children: loading ? 'Processing...' : confirmText })] })] }));
}
// Header icons (small, 20x20, wireframe style)
const HeaderIconSuccess = (jsxRuntimeExports.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": true, children: [jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }), jsxRuntimeExports.jsx("polyline", { points: "8 12 11 15 16 9", strokeLinecap: "round", strokeLinejoin: "round" })] }));
const HeaderIconError = (jsxRuntimeExports.jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", "aria-hidden": true, children: [jsxRuntimeExports.jsx("circle", { cx: "12", cy: "12", r: "10" }), jsxRuntimeExports.jsx("line", { x1: "9", y1: "9", x2: "15", y2: "15", strokeLinecap: "round" }), jsxRuntimeExports.jsx("line", { x1: "15", y1: "9", x2: "9", y2: "15", strokeLinecap: "round" })] }));
/**
* Body icon for success (56x56, wireframe Liquid Glass style)
*/
function BodyIconSuccess() {
return (jsxRuntimeExports.jsxs("svg", { width: "56", height: "56", viewBox: "0 0 48 48", fill: "none", "aria-hidden": true, style: { flexShrink: 0 }, children: [jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: "22", stroke: "url(#successGradient)", strokeWidth: "1.5", opacity: "0.6" }), jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: "18", stroke: "rgba(34, 197, 94, 0.3)", strokeWidth: "1", strokeDasharray: "4 2" }), jsxRuntimeExports.jsx("polyline", { points: "16 24 22 30 32 18", stroke: "rgba(34, 197, 94, 0.9)", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }), jsxRuntimeExports.jsx("defs", { children: jsxRuntimeExports.jsxs("linearGradient", { id: "successGradient", x1: "0%", y1: "0%", x2: "100%", y2: "100%", children: [jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "rgba(34, 197, 94, 0.8)" }), jsxRuntimeExports.jsx("stop", { offset: "50%", stopColor: "rgba(34, 197, 94, 0.4)" }), jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "rgba(34, 197, 94, 0.8)" })] }) })] }));
}
/**
* Body icon for error (56x56, wireframe Liquid Glass style)
*/
function BodyIconError() {
return (jsxRuntimeExports.jsxs("svg", { width: "56", height: "56", viewBox: "0 0 48 48", fill: "none", "aria-hidden": true, style: { flexShrink: 0 }, children: [jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: "22", stroke: "url(#errorGradient)", strokeWidth: "1.5", opacity: "0.6" }), jsxRuntimeExports.jsx("circle", { cx: "24", cy: "24", r: "18", stroke: "rgba(239, 68, 68, 0.3)", strokeWidth: "1", strokeDasharray: "4 2" }), jsxRuntimeExports.jsx("line", { x1: "18", y1: "18", x2: "30", y2: "30", stroke: "rgba(239, 68, 68, 0.9)", strokeWidth: "2.5", strokeLinecap: "round" }), jsxRuntimeExports.jsx("line", { x1: "30", y1: "18", x2: "18", y2: "30", stroke: "rgba(239, 68, 68, 0.9)", strokeWidth: "2.5", strokeLinecap: "round" }), jsxRuntimeExports.jsx("defs", { children: jsxRuntimeExports.jsxs("linearGradient", { id: "errorGradient", x1: "0%", y1: "0%", x2: "100%", y2: "100%", children: [jsxRuntimeExports.jsx("stop", { offset: "0%", stopColor: "rgba(239, 68, 68, 0.8)" }), jsxRuntimeExports.jsx("stop", { offset: "50%", stopColor: "rgba(239, 68, 68, 0.4)" }), jsxRuntimeExports.jsx("stop", { offset: "100%", stopColor: "rgba(239, 68, 68, 0.8)" })] }) })] }));
}
function ResultModal({ open, type, title, message, onClose, autoCloseDelay, buttonText, }) {
// Auto close after delay if specified
require$$0.useEffect(() => {
if (open && autoCloseDelay && autoCloseDelay > 0) {
const timer = setTimeout(onClose, autoCloseDelay);
return () => clearTimeout(timer);
}
}, [open, autoCloseDelay, onClose]);
const isSuccess = type === 'success';
const defaultButtonText = isSuccess ? 'Done' : 'Close';
return (jsxRuntimeExports.jsxs(Modal, { open: open, onClose: onClose, closeOnOverlay: true, style: { maxWidth: 480, width: '100%' }, children: [jsxRuntimeExports.jsx(ModalHeader, { title: title, align: "center", onClose: onClose, icon: isSuccess ? HeaderIconSuccess : HeaderIconError }), jsxRuntimeExports.jsx(ModalBody, { children: jsxRuntimeExports.jsxs("div", { style: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-start',
gap: 20,
padding: '12px 0'
}, children: [isSuccess ? jsxRuntimeExports.jsx(BodyIconSuccess, {}) : jsxRuntimeExports.jsx(BodyIconError, {}), jsxRuntimeExports.jsx(Typography, { variant: "body", color: "secondary", as: "span", children: message })] }) }), jsxRuntimeExports.jsx(ModalFooter, { children: jsxRuntimeExports.jsx(Button, { variant: "primary", onClick: onClose, children: buttonText ?? defaultButtonText }) })] }));
}
exports.ActionIcons = ActionIcons;
exports.BrandLogo = BrandLogo;
exports.Button = Button;
exports.CellRenderers = CellRenderers;
exports.ChoiceTile = ChoiceTile;
exports.CodeInput = CodeInput;
exports.ComboField = ComboField;
exports.ConfirmDialog = ConfirmDialog;
exports.DataTable = DataTable;
exports.DatePickerModal = DatePickerModal;
exports.DetailModal = DetailModal;
exports.FormField = FormField;
exports.FormGrid = FormGrid;
exports.FormModal = FormModal;
exports.FormSection = FormSection;
exports.GlassCard = GlassCard;
exports.InfoBox = InfoBox;
exports.InfoRow = InfoRow;
exports.InvitationChip = InvitationChip;
exports.JourneyCard = JourneyCard;
exports.JourneyProgress = JourneyProgress;
exports.MERIDIAN_CHART_COLORS = MERIDIAN_CHART_COLORS;
exports.MetricsEvolutionChart = MetricsEvolutionChart;
exports.Modal = Modal;
exports.ModalBody = ModalBody;
exports.ModalFooter = ModalFooter;
exports.ModalHeader = ModalHeader;
exports.OctahedronMark = OctahedronMark;
exports.ProfileCard = ProfileCard;
exports.ResultModal = ResultModal;
exports.Select = Select;
exports.TableHeader = TableHeader;
exports.TableRow = TableRow;
exports.ThemeToggle = ThemeToggle;
exports.Typography = Typography;
exports.VerificationScoreCard = VerificationScoreCard;
exports.VerificationShield = VerificationShield;
exports.VerificationTimeline = VerificationTimeline;
exports.asMetricNumber = asMetricNumber;
exports.auditLogTableConfig = auditLogTableConfig;
exports.citiesTableConfig = citiesTableConfig;
exports.cn = cn;
exports.computeNormalizedYDomain = computeNormalizedYDomain;
exports.countriesTableConfig = countriesTableConfig;
exports.getTableConfig = getTableConfig;
exports.octahedronFrame = octahedronFrame;
exports.parametersTableConfig = parametersTableConfig;
exports.regionsTableConfig = regionsTableConfig;
exports.statesTableConfig = statesTableConfig;
//# sourceMappingURL=index.js.map