Explorar el Código

前两个页面基础

18079408532 hace 1 año
padre
commit
62e3907582

+ 342 - 0
README.md

@@ -0,0 +1,342 @@
+# 生活智伴项目策划书
+- 姓名 陈志琪
+- 学号 202226701047
+- 班级 22软工大数据班
+- 手机 18079408532
+
+
+# 项目设想
+01*
+一句话描述项目设想(行业领域、用户画像、核心功能)
+我希望在日常生活中AI能成为一名生活助理,帮助安排日常事务,对于生活中的细节能给出建议,比如说提醒人们一些遗忘的事情,对于天气温度等已知事情的利用分析,成为人们生活中的一部分
+
+02 项目的业务流程
+  1.用户信息收集:用户通过智能设备输入个人基本信息、日常习惯、健康状况、兴趣爱好等数据,系统也可通过分析用户的历史行为(如日程安排、常去的地点、偏好的活动等)收集相关信息。
+需求明确:用户提出日常生活中希望改进或更高效的事务管理需求,例如日程安排、待办事项、购物清单、健康管理等,或者在特定时间段内需要提醒的事项,如重要会议、生日提醒等。
+  2.数据整合与分析:系统将整合多种数据来源,包括天气信息、交通状况、用户的设备数据(如手机日程、智能家居设备等),以及来自用户过往行为的数据。通过大数据分析和人工智能算法,生成符合用户习惯和需求的最佳建议。
+  3.个性化建议与提醒:根据用户的需求和偏好,系统提供具体的生活建议,如出行路线、天气适配的穿着建议、饮食安排、运动规划等,同时自动生成并提醒用户重要的待办事项。
+  4.方案展示与调整:系统展示日常生活规划方案,用户可以根据自己的时间、偏好、紧急程度等对建议进行修改调整。系统通过学习用户反馈,不断优化建议内容。
+  5.自动化执行与提醒:用户确认日程安排和建议后,系统可以自动与外部服务(如餐厅预订、交通服务、智能家居设备等)对接,提供及时的提醒和日程调整,确保用户的生活更加有序。
+  6.活动后反馈收集与优化:活动或日程安排结束后,系统会收集用户反馈,评估建议的有效性,并通过数据分析来改进日后的服务,优化个人化建议的准确性和及时性。
+03 项目的商业模式
+  1.付费订阅模式:用户可以通过订阅制支付一定费用,获得不同服务等级的生活助理服务。高级订阅用户可获得更精细化的个人日程管理、更高频次的提醒服务、更个性化的生活建议等。
+  2.按次收费模式:对于偶尔需要帮助安排日程或日常事务的用户,每次使用某项服务时支付相应费用,如单次的旅行规划、健康饮食建议等。
+  3.与商家合作分成:与餐厅、超市、健身房、出行平台等商家合作,推荐用户使用这些商家的服务,并根据用户消费金额收取一定的分成费用。例如,用户通过生活助理系统预定餐厅、购买食品或安排运动课程时,系统获得合作商家的返佣。
+  4.广告收入模式:在系统的通知或建议中,适当展示与用户兴趣相关的广告或推荐,例如旅行推荐、健康产品广告等。根据广告的点击和购买情况,系统可向广告商收取费用。
+  5.增值服务收费:如用户希望获取更个性化的生活建议或专属服务(如健康监控、心理咨询、家政服务、定制旅行等),可以支付额外的费用。
+04 相对可行的策略
+初期策略:
+  1.针对特定人群:选择上班族、年轻人、家庭主妇等特定人群作为目标用户群,针对他们的日常需求(如工作日程、家庭事务、购物需求等)提供个性化的生活助理服务。
+  2.免费试用与口碑营销:为新用户提供限时免费试用,鼓励他们分享体验和推荐给朋友,利用口碑传播吸引更多潜在用户。
+  3.与本地商家合作推广:与当地商家合作,通过提供优惠券、会员福利等方式吸引用户订阅服务,同时建立合作关系,为用户提供更多便捷服务。
+中期策略:
+  1.优化个性化推荐系统:根据用户行为、历史数据和反馈,优化生活助理的推荐引擎,提高智能化和精准度,让系统能够更好地适应用户的个性化需求。
+  2.拓展服务内容:根据用户需求,不断增加更多服务功能,如健康管理(饮食、运动等)、心理咨询、家庭日常管理等,满足用户在不同场景下的需求。
+  3.推出会员制度:设置不同等级的会员,提供优先处理、专属客服、更多定制化服务等特权,增加用户粘性。
+长期策略:
+  1.跨地区拓展:在本地市场获得一定的用户基础和口碑后,逐步将服务拓展到其他城市或国家,针对不同地区的用户需求进行本地化优化。
+  2.深度挖掘用户数据:通过收集和分析用户日常行为、偏好、健康状况等数据,为用户提供更加精准的服务。利用AI技术进行更深层次的预测和建议,甚至能够预知用户的需求变化。
+  3.构建社交功能:除了提供生活助理服务外,加入社交功能,允许用户分享日常经验、健康食谱、旅行计划等内容,增强用户之间的互动与社区感,进一步提高平台的活跃度和粘性。
+05
+用AI为项目起个好名字
+生活智伴
+## 项目背景
+1. 政策背景分析
+(1) 政策支持与政府推动
+随着数字化和人工智能技术的发展,政府在多个层面都出台了相关政策,鼓励创新型企业和科技项目。特别是在智能生活和健康管理领域,政府通过以下几方面提供了政策支持:
+
+人工智能发展规划:近年来,中国政府高度重视人工智能产业的推进,并已将其纳入国家科技创新规划,推动人工智能与各行各业的融合。中国《新一代人工智能发展规划》明确提出,要支持人工智能技术的产业化应用,特别是在智慧城市、智能家居、智能医疗等领域。
+
+智慧城市与智能家居建设:政策层面推动智慧城市建设,涵盖智能家居、智慧医疗、智慧交通等多个方向。生活智伴项目正符合这些发展趋势,有机会借助政策支持,打入政府推动的智能生活场景。
+
+数据保护与隐私法规:随着人工智能在个人生活中的应用,个人数据的隐私和安全成为政府关注的重要问题。例如,中国的《个人信息保护法》(PIPL)要求企业在使用用户数据时,必须确保数据的安全性和合法性。此项目需要在数据收集、存储、分析和使用方面严格遵守这些规定,以确保合规性。
+
+(2) 行业监管与合规问题
+随着智能助手和数据驱动型服务的发展,行业也有越来越多的监管要求。例如,关于人工智能算法的透明度、公平性以及在推荐系统中的伦理问题都需要关注。此外,个人健康数据的使用也涉及到医疗健康领域的法规和标准。因此,确保符合相关法规是项目实施中的重要一环。
+
+2. 行业背景分析
+(1) 人工智能与智能生活的快速发展
+近年来,人工智能技术在各个领域的应用不断扩展,特别是在智能生活和智能助手领域。像Alexa、Siri、Google Assistant这样的智能助手已被广泛应用于个人生活,用户逐渐习惯通过语音助手进行信息查询、日程安排、设备控制等操作。根据国际市场研究机构的预测,智能家居和个人智能助理市场将在未来几年继续增长。
+
+智能家居的普及:随着智能家居设备的普及,智能设备的数据可以被整合,形成一个更加全面的生活数据源。本项目的优势在于,能够基于多设备数据和人工智能算法,为用户提供个性化的生活建议和提醒,这正是当前智能生活市场的需求所在。
+
+健康管理的趋势:健康管理在当下成为了人们关注的焦点,尤其是基于大数据的个性化健康方案。AI可以结合用户的生物数据、运动习惯、饮食习惯等信息,提供定制化的健康建议,迎合了行业中越来越强的健康需求。
+
+(2) 竞争格局
+目前,市场上已有多个类似的产品和服务,主要包括:
+
+智能助手类产品:如苹果的Siri、Google Assistant、Amazon的Alexa等,这些产品都能够提供基本的日程安排、提醒、智能家居控制等服务,但其智能化程度、个性化服务和跨平台整合能力有待提高。
+
+专业生活助理与健康管理服务:如Somebody、LifeOmic等公司提供了定制化的健康管理和生活助理服务,但其一般聚焦于特定领域(如健康、健身、饮食等),没有全面涵盖日常生活的各个方面。
+
+本地化应用:在中国,像小米、华为等企业也推出了智能家居和个性化服务的整合平台,这些平台可能会成为潜在竞争者。
+# 产品定位
+
+## 用户分析
+
+### 目标用户群
+
+#### 上班族/年轻专业人群:
+- **需求**:忙碌的工作日程、会议安排、生活节奏快,容易忽略重要事项(如生日、健康检查等)。需要一款智能助手帮助管理时间和事务。
+- **行为特点**:频繁使用手机和智能设备,愿意尝试新技术,追求高效的生活方式。
+- **痛点**:时间管理困难、容易遗漏事项、想提升生活质量但缺乏时间。
+
+#### 家庭主妇/家庭管理者:
+- **需求**:需要一个便捷的方式来管理家庭日常事务、购物清单、家人日程、健康饮食等。
+- **行为特点**:关注家庭健康、子女教育、购物安排等。通常有较强的依赖性,对便利性和个性化的需求较高。
+- **痛点**:家庭事务琐碎且繁杂,时常需要记住大量细节,寻找一个智能化的助手来减轻压力。
+
+#### 老年人群体:
+- **需求**:需要关注健康、药物提醒、紧急联系、社交娱乐等方面的帮助。
+- **行为特点**:对科技的接受度较低,但对生活质量的要求较高,且更加依赖智能设备提供生活上的支持。
+- **痛点**:健康管理需要持续关注,社交需求较大,容易疏忽生活中的一些细节(如药物、日程等)。
+
+### 用户核心痛点
+- 忘记重要事项(会议、生日、约会等)
+- 难以高效管理日常事务(如购物清单、健康跟踪、家庭管理等)
+- 需要个性化的生活建议和及时的提醒
+- 生活繁忙,缺少时间优化或改善生活方式
+- 渴望更多自动化、智能化的服务来提升生活品质
+
+### 用户需求
+- 智能化、个性化的日程管理和任务提醒
+- 天气、健康、出行等数据驱动的生活建议
+- 快速高效的生活事务处理(如餐厅预定、购物推荐、交通安排等)
+- 高度可定制和实时反馈的系统
+## 主要功能
+### 1. 个性化日程管理与提醒
+- **功能**:自动同步和管理用户的日程安排,提供日常事务的提醒(会议、生日、待办事项等)。系统根据用户偏好和历史行为,推送定制化的提醒和建议。
+- **价值**:帮助用户高效管理时间,避免遗漏重要事项,提升生活和工作的效率。
+
+### 2. 基于数据的智能建议
+- **功能**:结合天气、健康、交通等数据,提供实时的生活建议。例如:穿衣推荐、出行路线、饮食规划、运动建议等。
+- **价值**:通过数据驱动的决策支持,帮助用户做出更加合理的生活选择,提升生活质量。
+
+### 3. 自动化服务对接
+- **功能**:与外部服务(如餐厅预订、交通安排、健康管理设备等)无缝对接,自动化执行用户的生活计划,减少用户手动操作。
+- **价值**:提高便利性,节省用户时间,同时增强智能助手的实际效用。
+
+### 4. 实时反馈与优化
+- **功能**:用户完成日常任务后,系统会根据反馈优化日后的建议,并根据用户的偏好调整推荐内容。
+- **价值**:通过持续学习和改进,提供更精准的服务,提升用户体验,增强用户粘性。
+
+### 5. 健康和家庭管理
+- **功能**:提供健康管理(如运动、饮食、健康监测)、家庭日常管理(如购物清单、家务安排等)功能,适合家庭用户或需要健康关注的用户。
+- **价值**:帮助家庭管理者或健康关注者保持更好的健康生活和家庭秩序,减少生活中的压力。
+## 商业模式
+### 1. 付费订阅模式
+- **适合目标人群**:对于需要长期使用生活助理的用户(如上班族、家庭主妇等),提供按月/年收费的订阅服务。
+- **订阅内容**:分层级设置不同的服务包,如:
+  - **基础版**(基本提醒、简单推荐)
+  - **高级版**(个性化推荐、紧急服务、优先处理等)
+  - **VIP版**(全方位定制服务、专属助理等)
+- **优势**:稳定的收入来源,且可以根据用户需求定制不同的服务包。
+
+### 2. 按次收费模式
+- **适合目标人群**:偶尔需要帮助的用户,或对订阅模式不感兴趣的用户。
+- **收费内容**:为用户提供按次计费的服务,如单次的健康饮食建议、旅行规划、家庭管理等。
+- **优势**:灵活性高,用户根据需求付费,适合轻度使用者。
+
+### 3. 与商家合作分成
+- **合作类型**:与餐饮、健身、交通、娱乐、零售等商家建立合作关系,向用户推荐商家服务(如餐厅预订、运动课程等),并根据用户的消费金额获取分成。
+- **优势**:通过与商家合作增加收入来源,用户也能享受到便捷服务和优惠。
+- **潜在风险**:可能会存在商家选择性不均衡的问题,因此需要精心筛选合作伙伴。
+
+### 4. 广告收入模式
+- **广告展示**:通过用户日常使用过程中提供相关广告,如健康、旅游、家庭用品等与用户兴趣匹配的广告。
+- **优势**:为平台提供额外收入来源。
+- **挑战**:需要平衡广告推送频率与用户体验,避免广告干扰影响用户使用感受。
+
+### 5. 增值服务收费
+- **服务内容**:提供更高端的个性化服务,如心理咨询、专业健康管理、家政服务等,用户可以根据需求支付额外费用。
+- **优势**:增加收入来源,且满足了用户的深层次需求。
+# 可行性分析
+## 一、项目概述
+生活智伴项目旨在通过AI技术为用户提供个性化的生活助理服务,帮助用户高效管理日常事务,提升生活质量。项目主要面向上班族、年轻人及家庭主妇等特定人群,核心功能包括日程管理、健康建议、天气提醒等。
+
+## 二、市场分析
+### 1. 目标市场
+- **用户群体**:上班族、年轻人、家庭主妇
+- **市场需求**:随着生活节奏加快,越来越多的人希望借助科技手段提升生活效率,减少琐事干扰。
+
+### 2. 竞争分析
+- **竞争对手**:现有的日程管理应用、健康管理软件等
+- **优势**:结合多种服务于一体的生活助理功能,提供个性化建议,提升用户体验。
+
+## 三、业务流程可行性
+### 1. 用户信息收集
+- **可行性**:通过智能设备和用户主动输入收集数据,技术上可行且用户接受度高。
+- **风险**:用户隐私保护需重视,需合规处理数据。
+
+### 2. 数据整合与分析
+- **可行性**:大数据与AI技术的成熟应用,能够实现数据整合与分析。
+- **风险**:数据来源的准确性和实时性可能影响分析结果。
+
+### 3. 个性化建议与提醒
+- **可行性**:AI算法能够根据用户习惯生成个性化建议,技术可行。
+- **风险**:建议的准确性依赖于数据质量,需不断优化算法。
+
+### 4. 方案展示与调整
+- **可行性**:用户可以根据个人需求调整建议,提升用户参与感。
+- **风险**:用户可能对系统建议的信任度不足,需增强用户体验。
+
+### 5. 自动化执行与提醒
+- **可行性**:与外部服务对接的技术实现可行,能提高用户便利性。
+- **风险**:外部服务的稳定性和接口变动可能影响服务质量。
+
+### 6. 活动后反馈收集与优化
+- **可行性**:通过用户反馈不断优化服务,形成良性循环。
+- **风险**:用户反馈的真实性和有效性需确保。
+
+## 四、商业模式可行性
+### 1. 付费订阅模式
+- **可行性**:市场上已有类似模式,用户愿意为优质服务付费。
+- **风险**:需明确服务价值,避免用户流失。
+
+### 2. 按次收费模式
+- **可行性**:适合偶尔需求的用户,灵活性高。
+- **风险**:可能导致用户粘性不足。
+
+### 3. 与商家合作分成
+- **可行性**:能为用户提供优惠,同时为平台带来收入。
+- **风险**:需建立稳固的商家合作关系。
+
+### 4. 广告收入模式
+- **可行性**:通过精准广告投放实现收入。
+- **风险**:需平衡用户体验与广告展示,避免用户反感。
+
+### 5. 增值服务收费
+- **可行性**:提供个性化增值服务,增加收入来源。
+- **风险**:需确保增值服务的实际价值。
+
+## 五、策略可行性
+### 初期策略
+- **针对特定人群**:明确目标用户,提高市场针对性。
+- **免费试用与口碑营销**:有效吸引用户,降低初期推广成本。
+- **与本地商家合作推广**:建立合作关系,增强市场渗透力。
+
+### 中期策略
+- **优化个性化推荐系统**:持续提升用户体验,增强系统智能化。
+- **拓展服务内容**:满足用户多样化需求,增强用户粘性。
+- **推出会员制度**:增加用户忠诚度,提高收入稳定性。
+
+### 长期策略
+- **跨地区拓展**:逐步扩大市场,增加用户基础。
+- **深度挖掘用户数据**:提升服务精准度,形成差异化竞争优势。
+- **构建社交功能**:增强用户互动,提高平台活跃度。
+
+## 六、结论
+整体来看,生活智伴项目在市场需求、技术可行性及商业模式上均具备较强的可行性。通过有效的市场推广、持续的产品迭代及用户反馈机制,能够逐步建立起用户基础并实现盈利。建议在项目推进过程中,注重用户隐私保护和数据安全,确保用户信任与满意度。
+# 产品结构
+## 一、项目概述
+生活智伴项目旨在通过AI技术为用户提供个性化的生活助理服务,帮助用户高效管理日常事务,提升生活质量。项目主要面向上班族、年轻人及家庭主妇等特定人群,核心功能包括日程管理、健康建议、天气提醒等。
+
+## 二、产品结构
+
+### 1. 用户界面
+- **用户注册与登录**
+  - 简单易用的注册流程,支持社交媒体账号登录。
+- **个人信息管理**
+  - 用户可以输入和修改个人基本信息、日常习惯、健康状况和兴趣爱好等。
+
+### 2. 数据收集模块
+- **信息输入**
+  - 用户通过智能设备(如手机、智能手表)输入数据。
+- **行为分析**
+  - 系统自动分析用户的历史行为(如日程安排、常去地点、偏好活动等)。
+
+### 3. 数据整合与分析模块
+- **数据源整合**
+  - 整合天气信息、交通状况、用户设备数据等多种信息来源。
+- **智能分析**
+  - 采用大数据分析和人工智能算法,生成符合用户习惯和需求的最佳建议。
+
+### 4. 个性化建议模块
+- **生活建议生成**
+  - 根据用户需求和偏好,提供出行路线、天气适配的穿着建议、饮食安排、运动规划等建议。
+- **待办事项提醒**
+  - 自动生成并提醒用户重要的待办事项(如会议、生日等)。
+
+### 5. 方案展示与调整模块
+- **日常生活规划展示**
+  - 系统展示个性化的生活规划方案,用户可视化调整建议。
+- **用户反馈收集**
+  - 收集用户对建议的反馈,优化建议内容。
+
+### 6. 自动化执行模块
+- **外部服务对接**
+  - 系统与餐厅预订、交通服务、智能家居设备等外部服务自动对接。
+- **实时提醒与调整**
+  - 提供及时的提醒和日程调整,确保用户的生活有序。
+
+### 7. 反馈与优化模块
+- **活动后反馈收集**
+  - 在活动或日程结束后收集用户反馈,评估建议的有效性。
+- **持续优化**
+  - 通过数据分析改进后续服务,提升个性化建议的准确性和及时性。
+
+## 三、商业模式结构
+
+### 1. 收入来源
+- **付费订阅模式**
+  - 用户通过订阅制支付费用,获得不同服务等级的生活助理服务。
+- **按次收费模式**
+  - 用户偶尔使用服务时支付相应费用。
+- **与商家合作分成**
+  - 与商家合作,根据用户消费金额收取分成费用。
+- **广告收入模式**
+  - 在系统中展示与用户兴趣相关的广告,根据点击和购买情况收取费用。
+- **增值服务收费**
+  - 提供个性化的增值服务,用户可支付额外费用获取。
+
+### 2. 用户获取策略
+- **目标用户群体**
+  - 针对上班族、年轻人、家庭主妇等特定人群。
+- **市场推广**
+  - 免费试用、口碑营销与本地商家合作推广。
+
+## 四、技术架构
+- **前端**
+  - 用户界面开发(移动端和Web端)。
+- **后端**
+  - 数据库管理、用户信息存储与处理、AI算法模型。
+- **云服务**
+  - 数据存储、计算资源和API接口的支持。
+
+## 五、总结
+生活智伴项目的产品结构涵盖了从用户信息收集到个性化建议生成、自动化执行及反馈优化的完整流程,通过合理的商业模式和技术架构支持,旨在为用户提供高效、便捷的生活助理服务。通过不断迭代和优化,提升用户体验,确保项目的可持续发展。
+# 生活智伴项目产品结构图
+
+```mermaid
+graph TD;
+    A[用户界面] --> B[用户注册与登录]
+    A --> C[个人信息管理]
+    
+    D[数据收集模块] --> E[信息输入]
+    D --> F[行为分析]
+    
+    G[数据整合与分析模块] --> H[数据源整合]
+    G --> I[智能分析]
+    
+    J[个性化建议模块] --> K[生活建议生成]
+    J --> L[待办事项提醒]
+    
+    M[方案展示与调整模块] --> N[日常生活规划展示]
+    M --> O[用户反馈收集]
+    
+    P[自动化执行模块] --> Q[外部服务对接]
+    P --> R[实时提醒与调整]
+    
+    S[反馈与优化模块] --> T[活动后反馈收集]
+    S --> U[持续优化]
+    
+    V[商业模式结构] --> W[收入来源]
+    V --> X[用户获取策略]
+    
+    A --> D
+    D --> G
+    G --> J
+    J --> M
+    M --> P
+    P --> S
+    S --> V

+ 4 - 1
angular.json

@@ -126,7 +126,10 @@
     }
   },
   "cli": {
-    "schematicCollections": ["@ionic/angular-toolkit"]
+    "schematicCollections": [
+      "@ionic/angular-toolkit"
+    ],
+    "analytics": false
   },
   "schematics": {
     "@ionic/angular-toolkit:component": {

+ 895 - 3
package-lock.json

@@ -9,10 +9,12 @@
       "version": "0.0.1",
       "dependencies": {
         "@angular/animations": "^18.0.0",
+        "@angular/cdk": "^17.0.0",
         "@angular/common": "^18.0.0",
         "@angular/compiler": "^18.0.0",
         "@angular/core": "^18.0.0",
-        "@angular/forms": "^18.0.0",
+        "@angular/forms": "^18.2.13",
+        "@angular/material": "^17.0.0",
         "@angular/platform-browser": "^18.0.0",
         "@angular/platform-browser-dynamic": "^18.0.0",
         "@angular/router": "^18.0.0",
@@ -476,6 +478,23 @@
         }
       }
     },
+    "node_modules/@angular/cdk": {
+      "version": "17.0.0",
+      "resolved": "https://registry.npmmirror.com/@angular/cdk/-/cdk-17.0.0.tgz",
+      "integrity": "sha512-8F1z0YhDjKWqB29+AUh33OKr2Ts5PVUUogVXEVmmPipPDtj82iIQLbtBnR6xpWvmsw9mS3oD76EwBoUhHQgi6g==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "optionalDependencies": {
+        "parse5": "^7.1.2"
+      },
+      "peerDependencies": {
+        "@angular/common": "^17.0.0 || ^18.0.0",
+        "@angular/core": "^17.0.0 || ^18.0.0",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
     "node_modules/@angular/cli": {
       "version": "18.2.12",
       "resolved": "https://registry.npmmirror.com/@angular/cli/-/cli-18.2.12.tgz",
@@ -649,6 +668,71 @@
         "node": "^18.19.1 || ^20.11.1 || >=22.0.0"
       }
     },
+    "node_modules/@angular/material": {
+      "version": "17.0.0",
+      "resolved": "https://registry.npmmirror.com/@angular/material/-/material-17.0.0.tgz",
+      "integrity": "sha512-rd7H7NhkDPDiyLHADm2FHOJlmgaWV7ZYNYPe/4yTXlt++GTSLhKus+PTCZYVsKGlA3mxDhNnC1RY+fdjtx/G2A==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/auto-init": "15.0.0-canary.a246a4439.0",
+        "@material/banner": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/button": "15.0.0-canary.a246a4439.0",
+        "@material/card": "15.0.0-canary.a246a4439.0",
+        "@material/checkbox": "15.0.0-canary.a246a4439.0",
+        "@material/chips": "15.0.0-canary.a246a4439.0",
+        "@material/circular-progress": "15.0.0-canary.a246a4439.0",
+        "@material/data-table": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dialog": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/drawer": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/fab": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/floating-label": "15.0.0-canary.a246a4439.0",
+        "@material/form-field": "15.0.0-canary.a246a4439.0",
+        "@material/icon-button": "15.0.0-canary.a246a4439.0",
+        "@material/image-list": "15.0.0-canary.a246a4439.0",
+        "@material/layout-grid": "15.0.0-canary.a246a4439.0",
+        "@material/line-ripple": "15.0.0-canary.a246a4439.0",
+        "@material/linear-progress": "15.0.0-canary.a246a4439.0",
+        "@material/list": "15.0.0-canary.a246a4439.0",
+        "@material/menu": "15.0.0-canary.a246a4439.0",
+        "@material/menu-surface": "15.0.0-canary.a246a4439.0",
+        "@material/notched-outline": "15.0.0-canary.a246a4439.0",
+        "@material/radio": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/segmented-button": "15.0.0-canary.a246a4439.0",
+        "@material/select": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/slider": "15.0.0-canary.a246a4439.0",
+        "@material/snackbar": "15.0.0-canary.a246a4439.0",
+        "@material/switch": "15.0.0-canary.a246a4439.0",
+        "@material/tab": "15.0.0-canary.a246a4439.0",
+        "@material/tab-bar": "15.0.0-canary.a246a4439.0",
+        "@material/tab-indicator": "15.0.0-canary.a246a4439.0",
+        "@material/tab-scroller": "15.0.0-canary.a246a4439.0",
+        "@material/textfield": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tooltip": "15.0.0-canary.a246a4439.0",
+        "@material/top-app-bar": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.3.0"
+      },
+      "peerDependencies": {
+        "@angular/animations": "^17.0.0 || ^18.0.0",
+        "@angular/cdk": "17.0.0",
+        "@angular/common": "^17.0.0 || ^18.0.0",
+        "@angular/core": "^17.0.0 || ^18.0.0",
+        "@angular/forms": "^17.0.0 || ^18.0.0",
+        "@angular/platform-browser": "^17.0.0 || ^18.0.0",
+        "rxjs": "^6.5.3 || ^7.4.0"
+      }
+    },
     "node_modules/@angular/platform-browser": {
       "version": "18.2.13",
       "resolved": "https://registry.npmmirror.com/@angular/platform-browser/-/platform-browser-18.2.13.tgz",
@@ -4707,6 +4791,808 @@
         "win32"
       ]
     },
+    "node_modules/@material/animation": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/animation/-/animation-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-0eV06UGYeuFwC/4t+yjg3LCRGRLq72ybBtJYzcBDpP4ASTjie0WmpAOFJYXRq2U5X/yxLviDMhpRemoSUjgZ0Q==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/auto-init": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/auto-init/-/auto-init-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-0QfmjT5elQ10hCxToVgq/WaC3301tVH1sJaO3O2yocVzr7s6iWm8/zch16V5hcHzQHbtcT3Rf4y1ZzmdNys2Iw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/banner": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/banner/-/banner-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-PBLgH7JEbEpTkLy33oyWXUhIFmSsdOrR6Gn6qIgQRo1qrnk5RSBGW2gEq4Z6793vjxM107gKudDb23E4Fcu4vg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/button": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/base": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/base/-/base-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-/ob3v3IFU8q2gGdVNWw5kNPjW2mRTeBIz1YdhGWUmRxKn2Kl8bdLOvrAmZtQMmPn/4cGXvinxpec/zVBWQKDkA==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/button": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/button/-/button-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-rGpVRde0Aqhv2t9QvT8Zl3HvG89BeUNPOpgfpaLBZ4SGGAO4rIrckl/eCENibKgmmdCKcYZlG9gc5abQVPfUvw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/card": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/card/-/card-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-+rYUnBPgv5QVF6BeUs3toIRdSwFVohGmjk2ptTXMZkKxqAJt7Nr9Znbm3Ym2hD8GUHJeh3pyGFvEs6rG6JMYAw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/checkbox": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/checkbox/-/checkbox-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-sQwHzm1TSxHUoPrqplWTk/BhyzdDhzcwlbucwJK9W0o9WXMDk+d9PvcCxpP/9sAnVqZk42BfE89Y0T1DHglZ9A==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/chips": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/chips/-/chips-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-TiV9WJ5taEHPGWPhXbxJvUJhLzThg+VpK7aAlvL4RurtmJ7pURuEdRS4Z6o0OEqi3wKQ4z/+K44kZUn/+9HALg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/checkbox": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "safevalues": "^0.3.4",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/circular-progress": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/circular-progress/-/circular-progress-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-+QTfyExPWzgm2tqMInd32qQOftsC1b8MUhAhZSfuecYBfqAc7KZkQEKa2nm4y8EHKMFWe8/DcxLV6IxMBLgHwA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/progress-indicator": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/data-table": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/data-table/-/data-table-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-89qVOjR7gqby6fsmh7tKj29SjQ2sGLXu2IzCeX3Vni4mz+xxo5dv11jxYNADvdgJDfhyDJFPh1FlqAH7O09nFA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/checkbox": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/icon-button": "15.0.0-canary.a246a4439.0",
+        "@material/linear-progress": "15.0.0-canary.a246a4439.0",
+        "@material/list": "15.0.0-canary.a246a4439.0",
+        "@material/menu": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/select": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/density": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/density/-/density-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-h8BJVCWkPR97WeWCN6/atVbSOP8J4+ZbbssidcwsnX7b3+3IaWdtBxGii25dsILX8pUVwwqxVis24y211b+8rg==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/dialog": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/dialog/-/dialog-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-4lyxd+5ccOEMUGKzZcssaYyzkCsYTpYCSQSANR0toQPLv3voDwKMfA709uZI6+nL7Re6Xdf7jx8qe+QpTTjVcw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/button": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/icon-button": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/dom": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/dom/-/dom-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-AftSOGQoQg/Ys2kOVjZzvqWmsnhg3Kam/2UC4Gj0DMMCu36J4MAoD+3PpnOd1aG3wiJKtUXR2vPIwE8I/PM9yg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/drawer": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/drawer/-/drawer-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-/JUmbzRBaikdbZ250yA9ZTPqp2W5nGvvuHYoNVAAmtOmxuwGvvNNpWiVZy2lIYeYcf1hA7hJ5mEQxs0aSD7iWQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/list": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/elevation": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/elevation/-/elevation-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-lwPIOb8fHyOljIWYcVLPT73dPIEOKat/CXu6gqYIVMQgZQIksQNUA7z1O3l7apkRSuYUOYSXqrgU7AnWP4KcJg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/fab": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/fab/-/fab-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-XUex3FNqxPD1i/4jITucB/RWTNkkdv52mbNmwrvbuThZlhuhyH9GzOQYTDop/b2783TPcv++xr8UUbuh8GWYzA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/feature-targeting": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/feature-targeting/-/feature-targeting-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-/SU9X5y8CRp6RS9qnjnM/N5qfsJ8bYILpR841eZmN6DLqMupaM9Yy7Mx8+v/QvpBLLhk+jmu79nFzwkwW54d6Q==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/floating-label": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/floating-label/-/floating-label-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-832qZ/qxKx0KUatoeVY3Q2NmboVgiWBG0/1VsbJyodHrgQWfnBOHgLE+M322o6uM3OhvO+kWm4iYbvwhmLZGsw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/focus-ring": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/focus-ring/-/focus-ring-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-ar0BtACFS3K14k/enAg0ePeEA/f/RJY4Ji4L/00Dw/B3XVpNRbqLH49jkcbtcQjdTS0FEyk2sWSNMZl6wVi0/A==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0"
+      }
+    },
+    "node_modules/@material/form-field": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/form-field/-/form-field-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-Q/+ErgtAUFUPPUmWA1m5IP5voiN8XjPRwyoAlFxSTa/4t+EA5B18Z8Bsn9b6I0AC8RHke06H7UWrKz8XUDIFpw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/icon-button": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/icon-button/-/icon-button-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-Igyo94rkIlqC91BR1Tv+WLTz1ZWcZZjl1xU7Vsx8mbWA1PnaRDUTNVV5LFi4e0ORp6GSblFTImpHngEy4agMEg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/image-list": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/image-list/-/image-list-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-Rcj3q7Tp7Nwbe5ht6ptTc3zqK8TSDJHaPDBf+kzi0kkh6MAB4qoHPgn+HnA+zIZ79CScU56bN7zjA6XYaZvsLw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/layout-grid": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/layout-grid/-/layout-grid-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-bkfxZuVzgtjEJgR3n8pvDQbe88ffULDJ5d2DF34IR8SOiRmQcj7UzqAt95XwIUcWlfisLCoIryP4U8XSpFb1EQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/line-ripple": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/line-ripple/-/line-ripple-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-20WmwRrejmtOdI37+959UqEVIjbMtAXlkDOkfCIA3OUhp+oZSjVkCqKxI16jxxVlnzJ353fy8xeSKzOHe4sExQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/linear-progress": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/linear-progress/-/linear-progress-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-IcCd4476pXHloTYadHDJ+2c2lntoVigeNnQEiD/ASQTKqKrJqkIdvvczFm9Ryu+V2+TKhp7vvQGFLUMaLPcmhw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/progress-indicator": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/list": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/list/-/list-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-4H5dKIjCUGIPmKjfcegV0SBybD5NNdHp26OU6sovvWIvxSGQtDJr6z9I7i+0vF/HIS5ScbHD2+9/txtL80iqCA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/menu": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/menu/-/menu-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-2HOHQAIdWQtXjSvEIrW3lnbcIwFf5XaQhFzCEZ04FcSGApc4iLwsmRFVW3PzWx+mVrUrEfO/K42DVULIX9J1Pg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/list": "15.0.0-canary.a246a4439.0",
+        "@material/menu-surface": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/menu-surface": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/menu-surface/-/menu-surface-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-4h4wZ0Rs7qBg1Otldw8ljp+LCULNL42pqbqcTXhKAkJM7pHcSw4k7IfoThSRLU3+V8T3/+qiAXyeQix2OGHzwg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/notched-outline": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/notched-outline/-/notched-outline-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-zmRZHJ+5cOWsBatRyK50wuht78olXySyKOJIIEmy8lxSMZefI1764u0mr8tS1KYF8vSAl5cUlwCC3/2Njz1FPg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/floating-label": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/progress-indicator": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/progress-indicator/-/progress-indicator-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-92HM5niUnqG5Y3M/xkscBD+2lkaWPDcIRPo0RHPYcyldL+EhWRv/sdQpfdiXw/h3uvKSowKxBMCHm8krAyf+sQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/radio": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/radio/-/radio-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-on8EVztWXc/ajcaowFZ31ClGADYxQrhj4ulMne0NxdHHWQ44ttf5aXOVqtv5mxeOzrRACOkQyTUXBG07yTWCEQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/ripple": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/ripple/-/ripple-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-Vl615/PIBpBD+IOI9Xypz0SV3RsmYJYSNx890Rih7irhUOaPsOUBmTYOWF5AsGBynqLcXoTNVhK92drYLKtJwQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/rtl": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/rtl/-/rtl-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-pgJFw8ZRpWGpwv7ZuBTJ+WdNmFBKoLVoMbbxKQWTHXVwhAqn3aoIq95o62T5QeEG/+sguNShdquG45CpAMmSRw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/segmented-button": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/segmented-button/-/segmented-button-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-oqGHs2C7C+yJW/xZf/wP8jBGLs6HcerhM3CsorLAEMH3MGuIlVC17WcisBewEWucsILYEWbySXy/7T4h6/psZA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/touch-target": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/select": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/select/-/select-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-odoNLiVOgdwbEeePkjHtlr43pjskDwyO8hi4z3jcud1Rg1czk5zoJ2mUI0+olOJjBQ26PGocwrSLqf3qaThbIA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/floating-label": "15.0.0-canary.a246a4439.0",
+        "@material/line-ripple": "15.0.0-canary.a246a4439.0",
+        "@material/list": "15.0.0-canary.a246a4439.0",
+        "@material/menu": "15.0.0-canary.a246a4439.0",
+        "@material/menu-surface": "15.0.0-canary.a246a4439.0",
+        "@material/notched-outline": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/shape": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/shape/-/shape-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-rcWPlCoHyP79ozeEKk73KWt9WTWdh6R68+n75l08TSTvnWZB5RRTmsI9BMkz55O9OJD/8H8ZsOxBe4x2QXUT7w==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/slider": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/slider/-/slider-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-is1BSBpxaXBBv+wSVpe9WGWmWl59yJEeDNubTES2UFD0er3BmA+PdKkL09vvytDnBcbKf77TbxaRiUSGVaKUQA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/snackbar": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/snackbar/-/snackbar-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-2NAtC1qozR/uajszZnPy08Ej8HNnpgvCjNCBerDN4SLH2Q0/aWrVrUjqRCp2ayAvsX+szoroGbCboMhaWRzDuQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/button": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/icon-button": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/switch": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/switch/-/switch-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-o0wcbYgm2yRs4een5uxT4RJnJ003DxXe33rk8vTBG2o7cdiSR3X7GJQxeIK3D9wPgWCAwBLhNYSzXrlTL5pkMw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "safevalues": "^0.3.4",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/tab": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/tab/-/tab-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-HGLK774uMeLnhbjDJBOjft7S6SurZnKb+6Und88OMDUVUEG6MkFBAKQQr09iBIeLE2sUAiGQhBVQtb7LJKwolQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/focus-ring": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/tab-indicator": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/tab-bar": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/tab-bar/-/tab-bar-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-dMQb1vXsBchQXcjbwgJZIGqTZHngm+3QGSOSb4LWjqHIgC5+w2RRrHsIAjNTyRhKssJ9nKKrbpM/Yz5vTPWH6w==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/tab": "15.0.0-canary.a246a4439.0",
+        "@material/tab-indicator": "15.0.0-canary.a246a4439.0",
+        "@material/tab-scroller": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/tab-indicator": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/tab-indicator/-/tab-indicator-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-gG2BgHT+ggKnUOaT8LjmH/+9nknRLh8v9qemrhUkDuCtZ8inlaC33OVbbxfrpQW3J+UzBh5YCUSC+2KrN39uUA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/tab-scroller": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/tab-scroller/-/tab-scroller-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-6KvBpalc4SwLbHFm0rnuIE64VffUj7AKhnPc+mqM6VmxOvDzQ/ZSYga0rWlUfM4mCDFX3ZkSxim+iNzVF+Ejaw==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/tab": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/textfield": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/textfield/-/textfield-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-4BW5bUERPlIeiPnLSby21h1/xDmySuAG9Ucn1LM801a0+5mK3IwWb8031AP3filKZZqTx5JJvOJYZd6/OWBJVA==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/density": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/floating-label": "15.0.0-canary.a246a4439.0",
+        "@material/line-ripple": "15.0.0-canary.a246a4439.0",
+        "@material/notched-outline": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/theme": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/theme/-/theme-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-HWxC5Nhz8JZKTLTVmAsNxIGB3Kzr53+YFMg327S8/XuEDmI0RFHFvtwM9rADmyrHFBmUaVhV4iELyxFdi67c9w==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/tokens": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/tokens/-/tokens-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-+5iGfQ51YSb0Qau8uC6/jHXCSC3enKaQKDf/iPHfuXAe04UznW3tmm1/Ju227aZXNISTJcnQYa2rpm1M14MeUg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/elevation": "15.0.0-canary.a246a4439.0"
+      }
+    },
+    "node_modules/@material/tooltip": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/tooltip/-/tooltip-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-Ja2Z4aZQkYWD6InXA+MG4M9zdKR6dYsXXlYzQppYpfcQzXylZqh5Y7WBLulG5fA2o83pHVwILfwFZM7j7ht08Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/button": "15.0.0-canary.a246a4439.0",
+        "@material/dom": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/tokens": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "safevalues": "^0.3.4",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/top-app-bar": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/top-app-bar/-/top-app-bar-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-twQchmCa1In/FFrALPYojgeM8vmV7KH96wRY9NmPSJ046ANgPCicLBgLuSzrLETCFqAwbztqzxSG4xMBL81rYg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/animation": "15.0.0-canary.a246a4439.0",
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/elevation": "15.0.0-canary.a246a4439.0",
+        "@material/ripple": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/shape": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "@material/typography": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/touch-target": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/touch-target/-/touch-target-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-ubyD1TUjZnRPEdDnk6Lrcm2ZsjnU7CV5y7IX8pj9IPawiM6bx4FkjZBxUvclbv3WiTGk5UOnwPOySYAJYAMQ1w==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/base": "15.0.0-canary.a246a4439.0",
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/rtl": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/@material/typography": {
+      "version": "15.0.0-canary.a246a4439.0",
+      "resolved": "https://registry.npmmirror.com/@material/typography/-/typography-15.0.0-canary.a246a4439.0.tgz",
+      "integrity": "sha512-eXzBl9ROzWZ+41nan5pCrn1C/Zq3o/VsrLFaGv8fdRmhRR6/wHMeuvCCwGf5VtEmWdAE9FpJzRU/4ZPiJCJUyg==",
+      "license": "MIT",
+      "dependencies": {
+        "@material/feature-targeting": "15.0.0-canary.a246a4439.0",
+        "@material/theme": "15.0.0-canary.a246a4439.0",
+        "tslib": "^2.1.0"
+      }
+    },
     "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
       "version": "3.0.3",
       "resolved": "https://registry.npmmirror.com/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
@@ -8761,7 +9647,7 @@
       "version": "4.5.0",
       "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
       "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
-      "dev": true,
+      "devOptional": true,
       "license": "BSD-2-Clause",
       "engines": {
         "node": ">=0.12"
@@ -14076,7 +14962,7 @@
       "version": "7.2.1",
       "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.2.1.tgz",
       "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
-      "dev": true,
+      "devOptional": true,
       "license": "MIT",
       "dependencies": {
         "entities": "^4.5.0"
@@ -15216,6 +16102,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/safevalues": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmmirror.com/safevalues/-/safevalues-0.3.4.tgz",
+      "integrity": "sha512-LRneZZRXNgjzwG4bDQdOTSbze3fHm1EAKN/8bePxnlEZiBmkYEDggaHbuvHI9/hoqHbGfsEA7tWS9GhYHZBBsw==",
+      "license": "Apache-2.0"
+    },
     "node_modules/sass": {
       "version": "1.77.6",
       "resolved": "https://registry.npmmirror.com/sass/-/sass-1.77.6.tgz",

+ 3 - 1
package.json

@@ -14,10 +14,12 @@
   "private": true,
   "dependencies": {
     "@angular/animations": "^18.0.0",
+    "@angular/cdk": "^17.0.0",
     "@angular/common": "^18.0.0",
     "@angular/compiler": "^18.0.0",
     "@angular/core": "^18.0.0",
-    "@angular/forms": "^18.0.0",
+    "@angular/forms": "^18.2.13",
+    "@angular/material": "^17.0.0",
     "@angular/platform-browser": "^18.0.0",
     "@angular/platform-browser-dynamic": "^18.0.0",
     "@angular/router": "^18.0.0",

+ 71 - 15
src/app/tab1/tab1.page.html

@@ -1,22 +1,78 @@
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
     <ion-title>任务管理</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="openAddTaskModal()">
+        <ion-icon name="add-outline"></ion-icon>
+        新建任务
+      </ion-button>
+    </ion-buttons>
   </ion-toolbar>
 </ion-header>
-
-<ion-content [fullscreen]="true">
-  <ion-item>
-    <ion-input [(ngModel)]="newTaskTitle" placeholder="添加新任务"></ion-input>
-    <ion-button (click)="addTask()">添加</ion-button>
-  </ion-item>
-
+<ion-content>
   <ion-list>
-    <ion-item *ngFor="let task of tasks">
-      <ion-checkbox [(ngModel)]="task.completed" (ionChange)="toggleTaskCompletion(task)"></ion-checkbox>
-      <ion-label [class.strikethrough]="task.completed">{{ task.title }}</ion-label>
-      <ion-button (click)="deleteTask(task.id)" slot="end" color="danger">删除</ion-button>
-    </ion-item>
+    <ion-item-sliding *ngFor="let task of tasks">
+      <ion-item>
+        <ion-checkbox [(ngModel)]="task.completed" (ionChange)="toggleTaskCompletion(task)"></ion-checkbox>
+        <ion-label class="task-label">
+          <h2 [class.strikethrough]="task.completed">{{ task.title }}</h2>
+          <p>开始: {{ task.startTime | date:'yyyy-MM-dd HH:mm' }}</p>
+          <p>截止: {{ task.endTime | date:'yyyy-MM-dd HH:mm' }}</p>
+        </ion-label>
+        <ion-badge [color]="getTaskStatus(task)">{{ getTaskStatusText(task) }}</ion-badge>
+      </ion-item>
+      
+      <ion-item-options side="end">
+        <ion-item-option color="danger" (click)="deleteTask(task.id)">
+          <ion-icon slot="icon-only" name="trash"></ion-icon>
+        </ion-item-option>
+      </ion-item-options>
+    </ion-item-sliding>
   </ion-list>
 
-  <app-explore-container name="Tab 1 page"></app-explore-container>
-</ion-content>
+  <div *ngIf="tasks.length === 0" class="ion-text-center ion-padding">
+    <p>暂无任务,点击右上角添加新任务</p>
+  </div>
+</ion-content>
+
+<ion-modal [isOpen]="isModalOpen">
+  <ng-template>
+    <ion-header>
+      <ion-toolbar>
+        <ion-title>新建任务</ion-title>
+        <ion-buttons slot="end">
+          <ion-button (click)="cancelModal()">取消</ion-button>
+        </ion-buttons>
+      </ion-toolbar>
+    </ion-header>
+
+    <ion-content class="ion-padding">
+      <ion-item>
+        <ion-label position="stacked">任务名称</ion-label>
+        <ion-input [(ngModel)]="newTask.title" placeholder="请输入任务名称"></ion-input>
+      </ion-item>
+
+      <ion-item>
+        <ion-label position="stacked">开始时间</ion-label>
+        <ion-datetime
+          [(ngModel)]="newTask.startTime"
+          presentation="date-time"
+          locale="zh-CN">
+        </ion-datetime>
+      </ion-item>
+
+      <ion-item>
+        <ion-label position="stacked">截止时间</ion-label>
+        <ion-datetime
+          [(ngModel)]="newTask.endTime"
+          presentation="date-time"
+          locale="zh-CN">
+        </ion-datetime>
+      </ion-item>
+
+      <ion-button expand="block" (click)="addTask()" class="ion-margin-top">
+        创建任务
+      </ion-button>
+    </ion-content>
+  </ng-template>
+</ion-modal>

+ 103 - 0
src/app/tab1/tab1.page.scss

@@ -0,0 +1,103 @@
+// 设置整体背景色
+ion-content {
+  --background: #f0f5ff;
+}
+
+// 美化任务卡片
+ion-item {
+  --background: rgba(255, 255, 255, 0.9);
+  margin: 8px 12px;
+  border-radius: 12px;
+  --border-radius: 12px;
+  --padding-start: 12px;
+  --inner-padding-end: 12px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+}
+
+// 美化复选框
+ion-checkbox {
+  --size: 22px;
+  --checkbox-background-checked: var(--ion-color-primary);
+  --border-radius: 6px;
+  --border-color: #d1d1d1;
+  --border-color-checked: var(--ion-color-primary);
+}
+
+// 任务标签样式
+.task-label {
+  margin-left: 12px;
+  
+  h2 {
+    margin: 0;
+    font-size: 16px;
+    font-weight: 500;
+    color: #2c3e50;
+  }
+  
+  p {
+    margin: 4px 0;
+    font-size: 13px;
+    color: #7f8c8d;
+  }
+}
+
+// 已完成任务样式
+.strikethrough {
+  text-decoration: line-through;
+  color: #bdc3c7;
+}
+
+// 状态标签样式
+ion-badge {
+  margin-left: 8px;
+  padding: 4px 8px;
+  border-radius: 6px;
+  font-weight: normal;
+  font-size: 12px;
+}
+
+// 空状态提示样式
+.ion-text-center {
+  margin-top: 40px;
+  
+  p {
+    color: #7f8c8d;
+    font-size: 15px;
+  }
+}
+
+// 模态框样式
+ion-modal {
+  ion-datetime {
+    margin: 8px 0;
+  }
+  
+  ion-input {
+    margin-top: 4px;
+  }
+  
+  ion-button {
+    margin: 20px 0;
+  }
+}
+
+// 顶部工具栏样式
+ion-toolbar {
+  --background: rgba(255, 255, 255, 0.95);
+  
+  ion-title {
+    font-weight: 600;
+    color: #2c3e50;
+  }
+}
+
+// 列表容器样式
+ion-list {
+  background: transparent;
+  padding: 8px 0;
+}
+
+// 滑动删除按钮样式
+ion-item-option {
+  --background: #ff6b6b;
+}

+ 83 - 20
src/app/tab1/tab1.page.ts

@@ -1,13 +1,14 @@
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonLabel, IonInput, IonButton, IonCheckbox } from '@ionic/angular/standalone';
-import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { CommonModule } from '@angular/common'; // 导入 CommonModule
+import { IonicModule } from '@ionic/angular';
 
 interface Task {
   id: number;
   title: string;
   completed: boolean;
+  startTime: string;
+  endTime: string;
 }
 
 @Component({
@@ -15,35 +16,97 @@ interface Task {
   templateUrl: 'tab1.page.html',
   styleUrls: ['tab1.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonLabel, IonInput, IonButton, IonCheckbox, ExploreContainerComponent, FormsModule, CommonModule], // 添加 CommonModule
+  schemas: [CUSTOM_ELEMENTS_SCHEMA],
+  imports: [
+    IonicModule,
+    CommonModule,
+    FormsModule
+  ]
 })
 export class Tab1Page {
   tasks: Task[] = [];
-  newTaskTitle: string = '';
+  isModalOpen = false;
+  newTask: Task = {
+    id: 0,
+    title: '',
+    completed: false,
+    startTime: new Date().toISOString(),
+    endTime: new Date().toISOString(),
+  };
 
   constructor() {
-    // 添加一些初始任务
-    this.tasks.push({ id: 1, title: '示例任务 1', completed: false });
-    this.tasks.push({ id: 2, title: '示例任务 2', completed: false });
+    const savedTasks = localStorage.getItem('tasks');
+    if (savedTasks) {
+      this.tasks = JSON.parse(savedTasks);
+    }
+  }
+
+  openAddTaskModal() {
+    this.isModalOpen = true;
+    this.newTask = {
+      id: Date.now(),
+      title: '',
+      completed: false,
+      startTime: new Date().toISOString(),
+      endTime: new Date().toISOString(),
+    };
+  }
+
+  cancelModal() {
+    this.isModalOpen = false;
   }
 
   addTask() {
-    if (this.newTaskTitle.trim()) {
-      const newTask: Task = {
-        id: this.tasks.length + 1,
-        title: this.newTaskTitle,
-        completed: false,
-      };
-      this.tasks.push(newTask);
-      this.newTaskTitle = '';
+    if (this.newTask.title.trim()) {
+      this.tasks.push({ ...this.newTask });
+      this.saveTasks();
+      this.isModalOpen = false;
     }
   }
 
+  deleteTask(taskId: number) {
+    this.tasks = this.tasks.filter(task => task.id !== taskId);
+    this.saveTasks();
+  }
+
   toggleTaskCompletion(task: Task) {
     task.completed = !task.completed;
+    this.saveTasks();
   }
 
-  deleteTask(taskId: number) {
-    this.tasks = this.tasks.filter(task => task.id !== taskId);
+  getTaskStatus(task: Task): string {
+    const now = new Date().getTime();
+    const startTime = new Date(task.startTime).getTime();
+    const endTime = new Date(task.endTime).getTime();
+
+    if (task.completed) {
+      return 'success';
+    } else if (now < startTime) {
+      return 'primary';
+    } else if (now >= startTime && now <= endTime) {
+      return 'warning';
+    } else {
+      return 'danger';
+    }
+  }
+
+  getTaskStatusText(task: Task): string {
+    const now = new Date().getTime();
+    const startTime = new Date(task.startTime).getTime();
+    const endTime = new Date(task.endTime).getTime();
+
+    if (task.completed) {
+      return '已完成';
+    } else if (now < startTime) {
+      return '未开始';
+    } else if (now >= startTime && now <= endTime) {
+      return '进行中';
+    } else {
+      return '已逾期';
+    }
   }
-}
+
+  private saveTasks() {
+    localStorage.setItem('tasks', JSON.stringify(this.tasks));
+  }
+}

+ 43 - 11
src/app/tab2/tab2.page.html

@@ -1,17 +1,49 @@
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
-    <ion-title>
-      Tab 2
-    </ion-title>
+    <ion-title>自律计时</ion-title>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 2</ion-title>
-    </ion-toolbar>
-  </ion-header>
+<ion-content>
+  <div class="timer-container">
+    <!-- 计时器显示 -->
+    <div class="timer-display">
+      <h1>{{ currentTime }}</h1>
+    </div>
 
-  <app-explore-container name="Tab 2 page"></app-explore-container>
+    <!-- 活动类型选择 -->
+    <div class="type-selector">
+      <ion-segment [(ngModel)]="selectedType" *ngIf="!isTimerRunning">
+        <ion-segment-button *ngFor="let type of activityTypes" [value]="type.id">
+          <ion-icon [name]="type.icon"></ion-icon>
+          <ion-label>{{ type.name }}</ion-label>
+        </ion-segment-button>
+      </ion-segment>
+    </div>
+
+    <!-- 控制按钮 -->
+    <div class="timer-controls">
+      <ion-button expand="block" (click)="startTimer()" *ngIf="!isTimerRunning" [disabled]="!selectedType">
+        开始计时
+      </ion-button>
+      <ion-button expand="block" color="danger" (click)="stopTimer()" *ngIf="isTimerRunning">
+        结束计时
+      </ion-button>
+    </div>
+  </div>
+
+  <!-- 历史记录 -->
+  <div class="records-container">
+    <h2 class="ion-padding">历史记录</h2>
+    <ion-list>
+      <ion-item *ngFor="let record of records">
+        <ion-icon slot="start" [name]="getTypeIcon(record.type)"></ion-icon>
+        <ion-label>
+          <h2>{{ getTypeName(record.type) }}</h2>
+          <p>{{ record.startTime | date:'yyyy-MM-dd HH:mm' }}</p>
+          <p>时长: {{ formatDuration(record.duration) }}</p>
+        </ion-label>
+      </ion-item>
+    </ion-list>
+  </div>
 </ion-content>

+ 78 - 0
src/app/tab2/tab2.page.scss

@@ -0,0 +1,78 @@
+.timer-container {
+  padding: 20px;
+  text-align: center;
+}
+
+.timer-display {
+  margin: 30px 0;
+  
+  h1 {
+    font-size: 48px;
+    font-weight: 300;
+    color: var(--ion-color-primary);
+  }
+}
+
+.type-selector {
+  margin-bottom: 20px;
+  
+  ion-segment {
+    background: var(--ion-color-light);
+    border-radius: 8px;
+    
+    ion-segment-button {
+      --padding-top: 8px;
+      --padding-bottom: 8px;
+      
+      ion-icon {
+        font-size: 24px;
+        margin-bottom: 4px;
+      }
+      
+      ion-label {
+        font-size: 12px;
+      }
+    }
+  }
+}
+
+.timer-controls {
+  margin: 20px 0;
+  
+  ion-button {
+    height: 48px;
+    --border-radius: 24px;
+    font-weight: 500;
+  }
+}
+
+.records-container {
+  h2 {
+    margin: 0;
+    font-size: 18px;
+    font-weight: 500;
+    color: var(--ion-color-dark);
+  }
+  
+  ion-item {
+    --padding-start: 16px;
+    --inner-padding-end: 16px;
+    
+    ion-icon {
+      font-size: 24px;
+      color: var(--ion-color-primary);
+    }
+    
+    h2 {
+      font-size: 16px;
+      font-weight: 500;
+      margin-bottom: 4px;
+    }
+    
+    p {
+      font-size: 14px;
+      color: var(--ion-color-medium);
+      margin: 2px 0;
+    }
+  }
+}

+ 142 - 5
src/app/tab2/tab2.page.ts

@@ -1,16 +1,153 @@
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
-import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { IonicModule } from '@ionic/angular';
+import { addIcons } from 'ionicons';
+import { 
+  schoolOutline, 
+  briefcaseOutline, 
+  moonOutline,
+  barbellOutline,
+  bookOutline,
+  codeSlashOutline,
+  leafOutline,
+  musicalNotesOutline,
+  languageOutline,
+  pencilOutline,
+  trashOutline,
+  addOutline
+} from 'ionicons/icons';
+
+interface TimerRecord {
+  id: number;
+  type: string;
+  startTime: string;
+  endTime: string;
+  duration: number; // 以分钟为单位
+}
 
 @Component({
   selector: 'app-tab2',
   templateUrl: 'tab2.page.html',
   styleUrls: ['tab2.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent]
+  schemas: [CUSTOM_ELEMENTS_SCHEMA],
+  imports: [IonicModule, CommonModule, FormsModule]
 })
 export class Tab2Page {
+  activityTypes = [
+    { id: 'study', name: '学习', icon: 'school-outline' },
+    { id: 'work', name: '工作', icon: 'briefcase-outline' },
+    { id: 'sleep', name: '睡眠', icon: 'moon-outline' },
+    { id: 'sport', name: '运动', icon: 'barbell-outline' },
+    { id: 'reading', name: '阅读', icon: 'book-outline' },
+    { id: 'coding', name: '编程', icon: 'code-slash-outline' },
+    { id: 'meditation', name: '冥想', icon: 'leaf-outline' },
+    { id: 'music', name: '音乐', icon: 'musical-notes-outline' },
+    { id: 'language', name: '语言', icon: 'language-outline' },
+    { id: 'writing', name: '写作', icon: 'pencil-outline' }
+  ];
+
+  selectedType: string = '';
+  isTimerRunning: boolean = false;
+  timerStartTime: Date | null = null;
+  currentTime: string = '00:00:00';
+  records: TimerRecord[] = [];
+  timer: any;
+
+  constructor() {
+    // 注册图标
+    addIcons({
+      'school-outline': schoolOutline,
+      'briefcase-outline': briefcaseOutline,
+      'moon-outline': moonOutline,
+      'barbell-outline': barbellOutline,
+      'book-outline': bookOutline,
+      'code-slash-outline': codeSlashOutline,
+      'leaf-outline': leafOutline,
+      'musical-notes-outline': musicalNotesOutline,
+      'language-outline': languageOutline,
+      'pencil-outline': pencilOutline,
+      'trash-outline': trashOutline,
+      'add-outline': addOutline
+    });
+
+    const savedRecords = localStorage.getItem('timerRecords');
+    if (savedRecords) {
+      this.records = JSON.parse(savedRecords);
+    }
+  }
+
+  selectType(typeId: string) {
+    this.selectedType = typeId;
+  }
+
+  startTimer() {
+    if (!this.selectedType) {
+      // 如果没有选择类型,提示用户
+      return;
+    }
+    
+    this.isTimerRunning = true;
+    this.timerStartTime = new Date();
+    
+    this.timer = setInterval(() => {
+      const now = new Date();
+      const diff = now.getTime() - this.timerStartTime!.getTime();
+      
+      const hours = Math.floor(diff / 3600000);
+      const minutes = Math.floor((diff % 3600000) / 60000);
+      const seconds = Math.floor((diff % 60000) / 1000);
+      
+      this.currentTime = `${this.padNumber(hours)}:${this.padNumber(minutes)}:${this.padNumber(seconds)}`;
+    }, 1000);
+  }
+
+  stopTimer() {
+    if (this.timer) {
+      clearInterval(this.timer);
+      const endTime = new Date();
+      const duration = Math.floor((endTime.getTime() - this.timerStartTime!.getTime()) / 60000); // 转换为分钟
+      
+      const record: TimerRecord = {
+        id: Date.now(),
+        type: this.selectedType,
+        startTime: this.timerStartTime!.toISOString(),
+        endTime: endTime.toISOString(),
+        duration: duration
+      };
+      
+      this.records.unshift(record);
+      this.saveRecords();
+      
+      this.isTimerRunning = false;
+      this.timerStartTime = null;
+      this.currentTime = '00:00:00';
+      this.selectedType = '';
+    }
+  }
+
+  private padNumber(num: number): string {
+    return num.toString().padStart(2, '0');
+  }
+
+  private saveRecords() {
+    localStorage.setItem('timerRecords', JSON.stringify(this.records));
+  }
+
+  getTypeName(typeId: string): string {
+    const type = this.activityTypes.find(t => t.id === typeId);
+    return type ? type.name : '';
+  }
 
-  constructor() {}
+  getTypeIcon(typeId: string): string {
+    const type = this.activityTypes.find(t => t.id === typeId);
+    return type ? type.icon : '';
+  }
 
+  formatDuration(minutes: number): string {
+    const hours = Math.floor(minutes / 60);
+    const mins = minutes % 60;
+    return hours > 0 ? `${hours}小时${mins}分钟` : `${mins}分钟`;
+  }
 }

+ 4 - 13
src/app/tab3/tab3.page.html

@@ -1,17 +1,8 @@
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
-    <ion-title>
-      Tab 3
-    </ion-title>
+    <ion-title>Tab 3</ion-title>
   </ion-toolbar>
 </ion-header>
-
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 3</ion-title>
-    </ion-toolbar>
-  </ion-header>
-
-  <app-explore-container name="Tab 3 page"></app-explore-container>
+<ion-content>
+  <div>Hello Tab 3</div>
 </ion-content>

+ 8 - 5
src/app/tab3/tab3.page.ts

@@ -1,5 +1,5 @@
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
+import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { IonicModule } from '@ionic/angular';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
 
 @Component({
@@ -7,8 +7,11 @@ import { ExploreContainerComponent } from '../explore-container/explore-containe
   templateUrl: 'tab3.page.html',
   styleUrls: ['tab3.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent],
+  schemas: [CUSTOM_ELEMENTS_SCHEMA],
+  imports: [IonicModule, ExploreContainerComponent]
 })
 export class Tab3Page {
-  constructor() {}
-}
+    constructor() {
+
+    }
+}